在Golang中复制文件的简单方法

Go有没有简单/快速的方法来复制文件?

我找不到Doc的快捷方式,search互联网也无济于事。

由于需要处理由目标操作系统及其configuration施加的一系列边缘情况和系统限制,所以健壮高效的副本在概念上是简单的,但并不容易实现。

如果你只是想复制现有的文件,你可以使用os.Link(srcName, dstName) 。 这样可以避免在应用程序中移动字节并节省磁盘空间。 对于大文件,这是一个重要的时间和空间节省。

但各种操作系统对于硬链接的工作方式有不同的限制。 根据您的应用程序和您的目标系统configuration, Link()调用可能无法在所有情况下工作。

如果您想要一个通用,健壮和高效的复制函数,请将Copy()更新为:

  1. 执行检查以确保至less某种forms的副本将成功(访问权限,目录存在等)
  2. 检查两个文件是否已经存在,并使用os.SameFile相同,如果相同,则返回成功
  3. 尝试一个链接,如果成功返回
  4. 复制字节(全部有效意味着失败),返回结果

优化的方法是在执行例程中复制字节,这样调用者就不会在字节拷贝上进行阻塞。 这样做会增加调用者处理成功/错误情况的复杂性。

如果我想这两个,我会有两个不同的复制function: CopyFile(src, dst string) (error)为阻止复制和CopyFileAsync(src, dst string) (chan c, error) ,将信令通道传回给调用者对于asynchronous情况。

 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 (eg, 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") } } 

你已经拥有了在标准库中编写这样一个函数所需的所有function。 这是明显的代码来做到这一点。

 // 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”

在这种情况下,有几个条件来validation,我更喜欢非嵌套的代码

 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

看一看吧 。

但请注意,它不会复制元数据。 也需要有人执行像移动的东西。

使用exec可能是值得的。