稳健和高效的副本在概念上是简单的,但是由于需要处理由目标操作系统及其配置施加的许多边缘情况和系统限制而不容易实现。
如果你只想复制现有的文件,可以使用os.Link(srcName,dstName)。这避免了在应用程序中移动字节并节省磁盘空间。对于大文件,这是一个重要的时间和空间节省。
但是各种操作系统对于如何使用硬链接有不同的限制。根据您的应用程序和目标系统配置,Link()调用可能无法在所有情况下工作。
如果你想要一个单一的通用,健壮和高效的复制功能,更新Copy()到:
>执行检查以确保至少某种形式的副本将成功(访问权限,目录存在等)
>检查两个文件是否已存在并且是否相同
os.SameFile,如果他们是相同的,返回成功
>尝试链接,如果成功则返回
>复制字节(所有有效的意思都失败),返回结果
优化将是在go程序中复制字节,因此调用者不会在字节副本上阻塞。这样做对调用者施加额外的复杂性以正确处理成功/错误情况。
如果我想要两个,我会有两个不同的副本函数:CopyFile(src,dst string)(错误)的阻塞副本和CopyFileAsync(src,dst字符串)(chan c,错误)通过一个信令通道回呼叫为异步情况。
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") } }