Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
File 复制文件的简单方法_File_Copy_Go - Fatal编程技术网

File 复制文件的简单方法

File 复制文件的简单方法,file,copy,go,File,Copy,Go,有什么简单/快速的方法可以在Go中复制文件吗 我在医生那里找不到快速的方法,搜索互联网也没有帮助 您已经在标准库中获得了编写此类函数所需的所有位。下面是显而易见的代码 // 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

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


我在医生那里找不到快速的方法,搜索互联网也没有帮助

您已经在标准库中获得了编写此类函数所需的所有位。下面是显而易见的代码

// 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()
}
警告:此答案主要是关于向文件添加第二个链接,而不是复制内容

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

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

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

如果您想要一个通用、健壮且高效的复制功能,请将
copy()
更新为:

  • 执行检查以确保至少某种形式的复制会成功(访问权限、目录存在等)
  • 检查这两个文件是否已经存在,并且是否相同
    os.SameFile
    ,如果它们相同,则返回success
  • 尝试链接,如果成功则返回
  • 复制字节(所有有效手段均失败),返回结果
  • 一种优化方法是在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")
        }
    }
    

    在本例中,有两个条件需要验证,我更喜欢非嵌套代码

    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)
    }
    

    如果您在linux/mac中运行代码,您可以只执行系统的cp命令

    srcFolder := "copy/from/path"
    destFolder := "copy/to/path"
    cpCmd := exec.Command("cp", "-rf", srcFolder, destFolder)
    err := cpCmd.Run()
    
    这有点像一个脚本,但它完成了任务。此外,您还需要导入“os/exec”

    查看

    但是请注意,它不会复制元数据。还需要有人来实施移动之类的事情


    仅使用exec可能是值得的。

    这里有一个复制文件的明显方法:

    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)
    }
    
    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\u windows中的包装。Go

    您可以使用“exec”。 用于windows的exec.Command(“cmd”、“/c”、“copy”、“fileToBeCopied destinationDirectory”) 我已经用过了,而且效果很好。有关exec的更多详细信息,请参阅手册。

    从Go 1.15(2020年8月)开始,您可以使用:


    根据应用程序的不同,如果输出文件存在,您可能希望失败,否则将覆盖文件的内容。您可以通过调用os.OpenFile(dst,syscall.O_CREATE | syscall.O_EXCL,FileMode(0666))而不是os.CREATE(…)。如果目标文件已存在,则该调用将失败。另一个优化是,如果两个文件已经相同(例如,如果链接了),则避免复制该文件。只有标准库无法实现的一个方面是对写时拷贝的透明支持,这在某些情况下可能是可取的。但是,c-o-w仅在某些文件系统上受支持,而且据我所知,它没有系统调用(除了ioctl)。延迟的
    out.Close()
    是否总是失败?您没有检查错误,但是文档说对
    Close()
    的连续调用将失败。为什么在返回和延迟中都有out.Close()?如果io.Copy错误,将运行第一个
    defer out.Close
    。第二个
    out.Close()
    是不必要的,每次都会出错。您应该添加一个大警告,即创建硬链接与创建副本不同。使用硬链接时,您有一个文件,使用副本时,您有两个不同的文件。使用副本时,对第一个文件的更改不会影响第二个文件。这一点很好。我认为链接的定义隐含了这一点,但这只有在已知的情况下才是明确的;不创建更多到它的分区链接。如果用户只是想从多个位置引用同一个文件,则硬链接(或软链接)应该是一个备选答案。理论上,您还应该检查dst中是否有足够的空间。请记住,由于这一部分:
    如果err=os.link(src,dst)….
    此函数无法按原样用于备份目的。如果要复制文件以备份某些数据,则必须在文件系统上复制数据本身。请讨论将文件内容复制到其他文件中。在这个问题下,这是否可以保证如果用户非法创建srcFolder或destFolder,会发生什么情况?假设destFolder:=“copy/to/path;rm-rf/”,SQL注入样式。如果从用户指定源文件夹和目标文件夹,我建议使用不同的方法。此代码假定路径有效。@user1047788但需要清除/验证来自用户的任何路径,以防您好奇,“;”不会被os.Exec计算为执行新命令。您的示例实际上会将确切的值“copy/to/path;rm-rf/”作为参数(包括空格和其他字符)发送到cp命令。这也是一个在windows上工作的巧妙技巧!不过,go-on windows将在srcFolder路径中进行名称替换,而go-on linux则不会。srcFolder:=“copy/from/path/*”在win上正常,在Linux上出错。我试图用您的代码复制文件
    --help
    ,但什么也没发生。;)你能再加一句吗
    package main
    import "os"
    
    func main() {
       o, e := os.Open("in.txt")
       if e != nil {
          panic(e)
       }
       defer o.Close()
       c, e := os.Create("out.txt")
       if e != nil {
          panic(e)
       }
       defer c.Close()
       c.ReadFrom(o)
    }