在Golang中实现文件复制的核心逻辑是读取源文件的内容,再将读取到的内容写入到目标文件中,不同的实现方式在性能和适用场景上存在差异,开发者可以根据实际需求选择合适的方案。

Golang实现文件复制的常见方法
1. 基础字节读写实现
最基础的文件复制方式是通过os.Open打开源文件,os.Create创建目标文件,然后循环读取源文件的字节内容写入目标文件,这种方式逻辑简单,适合理解文件复制的基本流程。
package main
import (
"fmt"
"io"
"os"
)
func copyFileByByte(srcPath, dstPath string) error {
// 打开源文件,只读模式
srcFile, err := os.Open(srcPath)
if err != nil {
return fmt.Errorf("打开源文件失败: %v", err)
}
defer srcFile.Close()
// 创建目标文件,默认权限0644
dstFile, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("创建目标文件失败: %v", err)
}
defer dstFile.Close()
// 定义每次读取的字节缓冲区,大小1KB
buf := make([]byte, 1024)
for {
// 从源文件读取内容到缓冲区
n, err := srcFile.Read(buf)
if err != nil && err != io.EOF {
return fmt.Errorf("读取源文件失败: %v", err)
}
if n == 0 {
// 读取到文件末尾,退出循环
break
}
// 将缓冲区内容写入目标文件
_, err = dstFile.Write(buf[:n])
if err != nil {
return fmt.Errorf("写入目标文件失败: %v", err)
}
}
return nil
}
func main() {
err := copyFileByByte("./test.txt", "./test_copy.txt")
if err != nil {
fmt.Println("文件复制失败:", err)
} else {
fmt.Println("文件复制成功")
}
}
2. 使用io.Copy函数实现
Golang标准库的io包提供了Copy函数,内部已经实现了高效的读写逻辑,不需要手动编写循环读取的代码,是实际开发中最常用的文件复制方式。
package main
import (
"fmt"
"io"
"os"
)
func copyFileByIoCopy(srcPath, dstPath string) error {
// 打开源文件
srcFile, err := os.Open(srcPath)
if err != nil {
return fmt.Errorf("打开源文件失败: %v", err)
}
defer srcFile.Close()
// 创建目标文件
dstFile, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("创建目标文件失败: %v", err)
}
defer dstFile.Close()
// 调用io.Copy完成文件复制,内部会自动处理读写和缓冲区
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return fmt.Errorf("复制文件失败: %v", err)
}
return nil
}
func main() {
err := copyFileByIoCopy("./source.txt", "./target.txt")
if err != nil {
fmt.Println("文件复制失败:", err)
} else {
fmt.Println("文件复制成功")
}
}
3. 带缓冲的优化实现
如果需要复制大文件,可以在io.Copy的基础上使用bufio包创建带缓冲的读写器,减少系统调用的次数,提升大文件复制的性能。
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func copyFileByBuffered(srcPath, dstPath string) error {
// 打开源文件
srcFile, err := os.Open(srcPath)
if err != nil {
return fmt.Errorf("打开源文件失败: %v", err)
}
defer srcFile.Close()
// 创建目标文件
dstFile, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("创建目标文件失败: %v", err)
}
defer dstFile.Close()
// 创建带缓冲的读取器和写入器,缓冲大小16KB
reader := bufio.NewReaderSize(srcFile, 16*1024)
writer := bufio.NewWriterSize(dstFile, 16*1024)
// 复制内容
_, err = io.Copy(writer, reader)
if err != nil {
return fmt.Errorf("复制文件失败: %v", err)
}
// 刷新缓冲,确保所有内容写入目标文件
err = writer.Flush()
if err != nil {
return fmt.Errorf("刷新缓冲失败: %v", err)
}
return nil
}
func main() {
err := copyFileByBuffered("./large_file.iso", "./large_file_copy.iso")
if err != nil {
fmt.Println("大文件复制失败:", err)
} else {
fmt.Println("大文件复制成功")
}
}
文件复制的注意事项
- 文件权限:使用
os.Create创建的目标文件默认权限是0644,如果需要保留源文件的权限,可以通过os.Stat获取源文件权限后,使用os.OpenFile指定权限创建目标文件。 - 错误捕获:文件操作中可能会遇到文件不存在、权限不足、磁盘空间不足等错误,需要做好错误捕获和处理,避免程序异常崩溃。
- 大文件处理:复制大文件时避免使用过大的缓冲区占用过多内存,合理设置缓冲区大小,推荐4KB到64KB之间。
- 目标文件覆盖:如果目标文件已经存在,
os.Create会直接覆盖原文件,如果需要避免覆盖,可以在复制前判断目标文件是否存在。
方法对比
| 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 基础字节读写 | 逻辑清晰,容易理解 | 代码冗余,性能一般 | 学习文件复制基础逻辑 |
| io.Copy实现 | 代码简洁,性能较好,内置优化 | 无法自定义缓冲细节 | 大部分常规文件复制场景 |
| 带缓冲优化实现 | 大文件复制性能更优 | 代码稍复杂 | 大文件复制场景 |