ZetCode

Go 复制文件

最后修改日期 2025 年 4 月 29 日

本教程演示了如何使用各种方法在 Golang 中复制文件。我们探讨了五种不同的文件复制方法,展示了不同的 Go 包和函数。每种方法根据用例提供独特的优势,例如简单性、性能或细粒度控制。

我们使用 Go 标准库中的函数,如 ioutil.ReadFileioutil.WriteFileio.CopyFile.ReadFile.Write 等,以高效地执行文件复制操作。

使用 ioutil 复制

第一个示例使用 ioutil 包来实现一种直接的方法。它将整个源文件读取到内存中,然后将其写入目标文件,由于其简单性和少量代码,非常适合小型文件。

copy_file.go
package main

import (
    "io/ioutil"
    "log"
)

func main() {

    src := "words.txt"
    dest := "words2.txt"

    bytesRead, err := ioutil.ReadFile(src)

    if err != nil {
        log.Fatal(err)
    }

    err = ioutil.WriteFile(dest, bytesRead, 0644)

    if err != nil {
        log.Fatal(err)
    }
}

ioutil.ReadFile 函数将源文件的全部内容读取到一个字节切片中,并在内部处理文件的打开和关闭。ioutil.WriteFile 函数将此字节切片写入目标文件,使用指定的权限 (0644) 创建或覆盖它。此方法简洁,但由于内存使用,可能不适用于非常大的文件。

使用 io.Copy

第二个示例采用 io.Copy 函数,该函数将数据从源文件流式传输到目标文件。此方法内存效率高,因为它避免将整个文件加载到内存中,因此适用于大型文件。

copy_file2.go
package main

import (
    "io"
    "log"
    "os"
)

func main() {

    src := "words.txt"
    dst := "words2.txt"

    fin, err := os.Open(src)
    if err != nil {
        log.Fatal(err)
    }
    defer fin.Close()

    fout, err := os.Create(dst)
    if err != nil {
        log.Fatal(err)
    }
    defer fout.Close()

    _, err = io.Copy(fout, fin)

    if err != nil {
        log.Fatal(err)
    }
}

我们使用 os.Open 打开源文件,使用 os.Create 创建或截断目标文件。它们返回文件句柄,这些句柄传递给 io.Copyio.Copy 以块的形式高效地流式传输数据。defer 语句确保在复制后关闭两个文件,防止资源泄漏。此方法平衡了简单性和性能。

使用 File.Read 和 File.Write

第三个示例使用 File.ReadFile.Write 来手动控制复制过程。它以块的形式读取文件,提供了在复制过程中处理数据的灵活性,这对于自定义转换或监控进度非常有用。

copy_file3.go
package main

import (
    "io"
    "log"
    "os"
)

func main() {

    src := "words.txt"
    dst := "words2.txt"

    buf := make([]byte, 1024)

    fin, err := os.Open(src)
    if err != nil {
        log.Fatal(err)
    }

    defer fin.Close()

    fout, err := os.Create(dst)
    if err != nil {
        log.Fatal(err)
    }

    defer fout.Close()

    for {

        n, err := fin.Read(buf)
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }

        if n == 0 {
            break
        }

        if _, err := fout.Write(buf[:n]); err != nil {
            log.Fatal(err)
        }
    }
}

创建一个 1024 字节的缓冲区,使用 File.Read 读取源文件的块。循环继续,直到没有读取到字节 (n == 0),表示文件结束。每个块使用 File.Write 写入目标文件。io.EOF 检查区分读取错误和文件结束。此方法提供了细粒度控制,但需要更多的代码。

使用 os.ReadFile 和 os.WriteFile

第四个示例使用 os.ReadFileos.WriteFile,这是已弃用的 ioutil 函数的现代替代品。此方法与第一个示例类似,但使用了更新的 API,确保与新 Go 版本的兼容性。

copy_file4.go
package main

import (
    "log"
    "os"
)

func main() {

    src := "words.txt"
    dst := "words2.txt"

    data, err := os.ReadFile(src)
    if err != nil {
        log.Fatal(err)
    }

    err = os.WriteFile(dst, data, 0644)
    if err != nil {
        log.Fatal(err)
    }
}

os.ReadFile 函数将源文件的全部内容读取到一个字节切片中,类似于 ioutil.ReadFileos.WriteFile 函数使用指定的权限 (0644) 将数据写入目标文件。此方法很简单,并且在现代 Go 程序中推荐用于小型文件,因为它使用了 os 包中未弃用的函数。

使用 io.CopyBuffer 复制

第五个示例将 io.CopyBuffer 与自定义缓冲区结合使用,提供了第二个示例的一个变体。它允许对缓冲区大小进行显式控制,这可以优化针对特定文件大小或硬件的性能。

copy_file5.go
package main

import (
    "io"
    "log"
    "os"
)

func main() {

    src := "words.txt"
    dst := "words2.txt"

    buf := make([]byte, 4096)

    fin, err := os.Open(src)
    if err != nil {
        log.Fatal(err)
    }
    defer fin.Close()

    fout, err := os.Create(dst)
    if err != nil {
        log.Fatal(err)
    }
    defer fout.Close()

    _, err = io.CopyBuffer(fout, fin, buf)
    if err != nil {
        log.Fatal(err)
    }
}

我们分配了一个 4096 字节的缓冲区用于复制,比 io.Copy 使用的默认缓冲区大。io.CopyBuffer 函数使用此缓冲区将数据从源文件流式传输到目标文件。此方法对大型文件来说很高效,并且允许调整缓冲区大小以提高性能,同时保持流式方法的简单性。

来源

Go io 包 - 参考

在本文中,我们在 Golang 中复制了文件。

作者

我的名字是 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了 1400 多篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

列出所有 Go 教程