复制文件的简单方法

新手上路,请多包涵

有什么简单/快速的方法可以在 Go 中复制文件吗?

我在文档中找不到快速方法,搜索互联网也无济于事。

原文由 herb 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 591
2 个回答

警告:这个答案主要是关于向文件添加硬链接,而不是关于复制内容。

一个 健壮高效 的副本在概念上很简单,但由于需要处理目标操作系统及其配置强加的许多边缘情况和系统限制,因此实施起来并不简单。

如果您只想复制现有文件,可以使用 os.Link(srcName, dstName) 。这避免了在应用程序中移动字节并节省磁盘空间。对于大文件,这可以显着节省时间和空间。

但是各种操作系统对硬链接的工作方式有不同的限制。根据您的应用程序和目标系统配置, Link() 调用可能并非在所有情况下都有效。

如果你想要一个单一的通用、健壮和高效的复制功能, Copy() 更新为:

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

一种优化方法是在 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")
    }
}

原文由 markc 发布,翻译遵循 CC BY-SA 4.0 许可协议

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, may cause OOM for a large file.
    data, err := ioutil.ReadFile(src)
    checkErr(err)
    // Write data to dst
    err = ioutil.WriteFile(dst, data, 0644)
    checkErr(err)
}

原文由 haosdent 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题