【发布时间】:2014-01-30 09:47:07
【问题描述】:
有没有什么简单/快速的方法可以在 Go 中复制文件?
我在 Doc 中找不到快速的方法,搜索互联网也无济于事。
【问题讨论】:
-
人们还讨论了在这个问题stackoverflow.com/questions/1821811/… 下将文件的内容复制到其他文件中
有没有什么简单/快速的方法可以在 Go 中复制文件?
我在 Doc 中找不到快速的方法,搜索互联网也无济于事。
【问题讨论】:
警告:这个答案主要是关于向文件添加硬链接,而不是复制内容。
健壮和高效副本在概念上很简单,但由于需要处理许多边缘情况和系统限制,因此实施起来并不简单。目标操作系统及其配置。
如果您只是想复制现有文件,您可以使用os.Link(srcName, dstName)。这避免了在应用程序中移动字节并节省磁盘空间。对于大文件,这可以节省大量时间和空间。
但是不同的操作系统对硬链接的工作方式有不同的限制。根据您的应用程序和目标系统配置,Link() 调用可能并非在所有情况下都有效。
如果您想要一个通用、强大且高效的复制功能,请将Copy() 更新为:
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")
}
}
【讨论】:
if err = os.Link(src, dst)... 此功能无法按原样用于备份目的。如果要复制文件以备份某些数据,则必须将数据本身复制到文件系统上
您已经获得了在标准库中编写此类函数所需的所有内容。这是显而易见的代码。
// 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()
}
【讨论】:
out.Close() 不会总是失败吗?您没有检查错误,但文档说连续调用 Close() 将失败。
defer out.Close 将运行。第二个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()
它把 go 有点像一个脚本,但它完成了工作。另外,你需要导入“os/exec”
【讨论】:
--help,但没有任何反应。 ;)
在这种情况下,需要验证几个条件,我更喜欢非嵌套代码
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)
}
【讨论】:
从 Go 1.15(2020 年 8 月)开始,您可以使用File.ReadFrom:
package main
import "os"
func main() {
r, err := os.Open("in.txt")
if err != nil {
panic(err)
}
defer r.Close()
w, err := os.Create("out.txt")
if err != nil {
panic(err)
}
defer w.Close()
w.ReadFrom(r)
}
【讨论】:
这是一个复制文件的明显方法:
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)
}
}
【讨论】:
eFile 中的e 是什么意思?
如果你在 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.Command("cmd","/c","copy","fileToBeCopied destinationDirectory") 用于 windows 我已经使用它并且它工作正常。有关 exec 的更多详细信息,您可以参考手册。
【讨论】: