Go语言 复制文件的简单方法

hjzp0vay  于 2022-12-07  发布在  Go
关注(0)|答案(9)|浏览(158)

在Go语言中,有没有简单/快速的方法来复制文件?
我找不到一个快速的方法在医生的和搜索互联网也没有帮助。

p5fdfcr1

p5fdfcr11#

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

  • 强健 * 且 * 高效 * 的拷贝在概念上很简单,但由于需要处理大量边缘情况以及目标操作系统及其配置所施加的系统限制,因此实施起来并不简单。

如果你只是想复制一个已有的文件,你可以使用os.Link(srcName, dstName)。这避免了在应用程序中移动字节,节省了磁盘空间。对于大文件,这是一个显着的时间和空间节省。
但是不同的操作系统对硬链接的工作方式有不同的限制。根据你的应用程序和目标系统配置,Link()调用可能不是在所有情况下都能工作。
如果您需要一个通用、可靠且高效的复制功能,请将Copy()更新为:
1.执行检查以确保至少某种形式的复制会成功(访问权限、目录存在等)
1.使用os.SameFile检查两个文件是否已存在并且相同,如果相同则返回成功
1.尝试链接,如果成功则返回
1.复制字节数(所有有效方式均失败),返回结果
一种优化方法是在go例程中复制字节,这样调用者就不会阻塞字节复制,这样做会增加调用者处理成功/错误情况的复杂性。
如果我同时需要这两种功能,我将拥有两种不同的复制功能:x1M4 N1 x用于分块复制,而x1M5 N1 x用于异步情况,其将信令信道传递回调用者。

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

sq1bmfud2#

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

baubqpgj3#

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

srcFolder := "copy/from/path"
destFolder := "copy/to/path"
cpCmd := exec.Command("cp", "-rf", srcFolder, destFolder)
err := cpCmd.Run()

它把go当作一个脚本,但是它完成了任务。

ryevplcw

ryevplcw4#

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

vi4fp9gy5#

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

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

mspsb9vt6#

  • 使用io.Copy在数据流中执行复制。
  • 关闭所有打开的文件描述符。
  • 所有应该检查的错误 * 都 * 被检查了,包括延迟的(*os.File).Close调用中的错误。
  • 优雅地处理多个非零错误,例如来自io.Copy(*os.File).Close的非零错误。
  • 没有其他答案中出现的不必要的复杂情况,例如对同一个文件调用Close两次,但忽略其中一次调用的错误。
  • 不需要使用stat检查是否存在或文件类型(例如常规、命名管道和目录)。这些检查不是必需的:如果openread操作对该文件类型无效,则将来的openread操作无论如何都会返回错误。其次,此类检查容易出现竞争(例如,文件可能在statopen之间的时间内被删除)。
  • 准确的文档注解。请参阅:“file”、“regular file”和dstpath存在时的行为。doc注解也与package os中其他函数的样式相匹配。
// Copy copies the contents of the file at srcpath to a regular file at dstpath.
// If dstpath already exists and is not a directory, the function truncates it. 
// The function does not copy file modes or file attributes.
func Copy(srcpath, dstpath string) (err error) {
    r, err := os.Open(srcpath)
    if err != nil {
        return err
    }
    defer r.Close() // ok to ignore error: file was opened read-only.

    w, err := os.Create(dstpath)
    if err != nil {
        return err
    }

    defer func() {
        c := w.Close()
        // Report the error from Close, if any.
        // But do so only if there isn't already
        // an outgoing error.
        if c != nil && err == nil {
            err = c
        }
    }()

    _, err = io.Copy(w, r)
    return err
}
6psbrbz9

6psbrbz97#

如果您使用的是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中的 Package 器

iklwldmw

iklwldmw8#

下面是复制文件的一个明显方法:

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

qqrboqgw9#

你可以使用“exec”。exec。命令(“cmd”,"/c”,“copy”,“fileToBeCopied destinationDirectory”)为窗口我用过这个,它工作得很好。你可以参考手册了解更多关于exec的细节。

相关问题