Чи існує простий / швидкий спосіб копіювання файлу в Go?
Я не зміг знайти швидкий шлях у документах, і пошук в Інтернеті також не допомагає.
Відповіді:
Попередження: Ця відповідь в основному стосується додавання другого посилання до файлу, а не копіювання вмісту.
Надійна і ефективна копія концептуально проста, але не просто реалізувати з - за необхідності обробляти ряд крайових випадків і системних обмежень, що накладаються на цільової операційної системи і його конфігурації.
Якщо ви просто хочете створити дублікат існуючого файлу, ви можете використовувати його os.Link(srcName, dstName)
. Це дозволяє уникнути необхідності переміщувати байти в додатку та економить місце на диску. Для великих файлів це істотна економія часу та місця.
Але різні операційні системи мають різні обмеження щодо роботи жорстких посилань. Залежно від програми та конфігурації цільової системи, Link()
дзвінки можуть працювати не у всіх випадках.
Якщо вам потрібна одна загальна, надійна та ефективна функція копіювання, оновіть Copy()
до:
os.SameFile
, поверніть успіх, якщо вони однаковіОптимізацією було б скопіювати байти в режимі go, щоб абонент не блокував байтову копію. Це накладає додаткові складності на абонента для належного розгляду справи про успіх / помилку.
Якби я хотів обидва, я мав би дві різні функції копіювання: CopyFile(src, dst string) (error)
для блокуючої копії і CopyFileAsync(src, dst string) (chan c, error)
яка передає канал сигналізації назад абоненту для асинхронного випадку.
package main
import (
"fmt"
"io"
"os"
)
// CopyFile copies a file from src to dst. If src and dst files exist, and are
// the same, then return success. Otherise, attempt to create a hard link
// between the two files. If that fail, copy the file contents from src to dst.
func CopyFile(src, dst string) (err error) {
sfi, err := os.Stat(src)
if err != nil {
return
}
if !sfi.Mode().IsRegular() {
// cannot copy non-regular files (e.g., directories,
// symlinks, devices, etc.)
return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
}
dfi, err := os.Stat(dst)
if err != nil {
if !os.IsNotExist(err) {
return
}
} else {
if !(dfi.Mode().IsRegular()) {
return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
}
if os.SameFile(sfi, dfi) {
return
}
}
if err = os.Link(src, dst); err == nil {
return
}
err = copyFileContents(src, dst)
return
}
// copyFileContents copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFileContents(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return
}
defer func() {
cerr := out.Close()
if err == nil {
err = cerr
}
}()
if _, err = io.Copy(out, in); err != nil {
return
}
err = out.Sync()
return
}
func main() {
fmt.Printf("Copying %s to %s\n", os.Args[1], os.Args[2])
err := CopyFile(os.Args[1], os.Args[2])
if err != nil {
fmt.Printf("CopyFile failed %q\n", err)
} else {
fmt.Printf("CopyFile succeeded\n")
}
}
if err = os.Link(src, dst)...
Ця функція не працюватиме для резервного копіювання, як є. Якщо ви хочете скопіювати файли, щоб зробити резервну копію деяких даних, ви повинні скопіювати самі дані у файловій системі
У вас є всі біти, необхідні для запису такої функції у стандартній бібліотеці. Ось очевидний код для цього.
// Copy the src file to dst. Any existing file will be overwritten and will not
// copy file attributes.
func Copy(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, in)
if err != nil {
return err
}
return out.Close()
}
out.Close()
завжди відстрочені відмовлять? Ви не перевіряєте помилку, але в документації сказано, що послідовні дзвінки на Close()
не зможуть.
import (
"io/ioutil"
"log"
)
func checkErr(err error) {
if err != nil {
log.Fatal(err)
}
}
func copy(src string, dst string) {
// Read all content of src to data
data, err := ioutil.ReadFile(src)
checkErr(err)
// Write data to dst
err = ioutil.WriteFile(dst, data, 0644)
checkErr(err)
}
Якщо ви запускаєте код у Linux / mac, ви можете просто виконати команду cp системи.
srcFolder := "copy/from/path"
destFolder := "copy/to/path"
cpCmd := exec.Command("cp", "-rf", srcFolder, destFolder)
err := cpCmd.Run()
Це трактування схоже на сценарій, але це робить роботу. Крім того, вам потрібно імпортувати "os / exec"
--help
із вашим кодом, і нічого не сталося. ;)
У цьому випадку є кілька умов для перевірки, я віддаю перевагу невкладеному коду
func Copy(src, dst string) (int64, error) {
src_file, err := os.Open(src)
if err != nil {
return 0, err
}
defer src_file.Close()
src_file_stat, err := src_file.Stat()
if err != nil {
return 0, err
}
if !src_file_stat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
dst_file, err := os.Create(dst)
if err != nil {
return 0, err
}
defer dst_file.Close()
return io.Copy(dst_file, src_file)
}
Це так просто, як показано нижче:
func CopyFile(source string, dest string) {
//Read the file
temp, _ := ioutil.ReadFile(source)
ioutil.WriteFile(dest, temp, 0777)
}
CopyFile("source/abc.txt", "dest/abc.txt")
Ось очевидний спосіб скопіювати файл:
package main
import (
"os"
"log"
"io"
)
func main() {
sFile, err := os.Open("test.txt")
if err != nil {
log.Fatal(err)
}
defer sFile.Close()
eFile, err := os.Create("test_copy.txt")
if err != nil {
log.Fatal(err)
}
defer eFile.Close()
_, err = io.Copy(eFile, sFile) // first var shows number of bytes
if err != nil {
log.Fatal(err)
}
err = eFile.Sync()
if err != nil {
log.Fatal(err)
}
}
e
in eFile
?
Якщо у вас є вікна, ви можете обернути CopyFileW так:
package utils
import (
"syscall"
"unsafe"
)
var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procCopyFileW = modkernel32.NewProc("CopyFileW")
)
// CopyFile wraps windows function CopyFileW
func CopyFile(src, dst string, failIfExists bool) error {
lpExistingFileName, err := syscall.UTF16PtrFromString(src)
if err != nil {
return err
}
lpNewFileName, err := syscall.UTF16PtrFromString(dst)
if err != nil {
return err
}
var bFailIfExists uint32
if failIfExists {
bFailIfExists = 1
} else {
bFailIfExists = 0
}
r1, _, err := syscall.Syscall(
procCopyFileW.Addr(),
3,
uintptr(unsafe.Pointer(lpExistingFileName)),
uintptr(unsafe.Pointer(lpNewFileName)),
uintptr(bFailIfExists))
if r1 == 0 {
return err
}
return nil
}
Код натхненний обгортками C:\Go\src\syscall\zsyscall_windows.go
Ви можете використовувати "exec". exec.Command ("cmd", "/ c", "copy", "fileToBeCopied destinationDirectory") для Windows, я використовував цей файл і працює нормально. Ви можете звернутися до керівництва для отримання детальної інформації про exec.
Погляньте на go-shutil .
Але майте на увазі, він не копіює метадані. Також потрібен хтось для реалізації таких речей, як переїзд.
Можливо, варто просто використовувати exec.