Простой способ скопировать файл в Golang
есть ли простой / быстрый способ скопировать файл в Go?
Я не мог найти быстрый способ в доке, и поиск в интернете тоже не помогает.
8 ответов:
A надежная и эффективное копировать концептуально просто, но не просто реализовать из-за необходимости обрабатывать ряд крайних случаев и системных ограничений, которые налагаются целевой операционной системой и ее конфигурацией.
если вы просто хотите сделать копию существующего файла, вы можете использовать
os.Link(srcName, dstName)
. Это позволяет избежать необходимости перемещать байты в приложении и экономит дисковое пространство. Для больших файлов, это значительное время и экономия пространства.но различные операционные системы имеют различные ограничения на то, как тяжело одной работать. В зависимости от вашего приложения и конфигурации целевой системы,
Link()
вызовы могут работать не во всех случаях.если вам нужна одна общая, надежная и эффективная функция копирования, обновите
Copy()
to:
- выполните проверки, чтобы убедиться, что хотя бы какая-то форма копирования будет успешной (права доступа, каталоги существуют и т. д.)
- Регистрация чтобы увидеть, если оба файла уже существуют и являются одинаковыми с помощью
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") } }
У вас есть все биты, необходимые для записи такой функции в стандартной библиотеке. Вот очевидный код для этого.
// 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() }
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"
в этом случае есть несколько условий для проверки, я предпочитаю не вложенный код
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) }
вот очевидный способ скопировать файл:
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) } }
Если вы находитесь на windows, вы можете обернуть 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
посмотреть go-shutil.
но имейте в виду, он не копирует метаданные, хотя. Также нужен кто-то, чтобы реализовать такие вещи, как двигаться.
может быть стоит просто использовать exec.