警告:此答案主要是关于向文件添加硬链接,而不是关于复制内容.
robust和efficient拷贝在概念上很简单,但实现起来并不简单,因为需要处理目标操作系统及其配置施加的大量边缘情况和系统限制.
如果您只是想复制现有文件,可以使用os.Link(srcName, dstName)
.这避免了在应用程序中四处移动字节,并节省了磁盘空间.对于大型文件,这将大大节省时间和空间.
但不同的操作系统对硬链接的工作方式有不同的限制.根据您的应用程序和目标系统配置,Link()
个调用可能并非在所有情况下都有效.
如果您需要单一的通用、健壮且高效的复制功能,请更新Copy()
以:
os.SameFile
,如果相同则返回SUCCESS一种优化方法是在go routine 中复制字节,这样调用者就不会阻塞字节复制.这样做会增加调用者正确处理成功/错误 case 的复杂性.
如果我两个都想要,我会有两个不同的复制功能: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")
}
}