GO语言
01GO基础-001GO语言简介
01GO基础-002语言环境安装
01GO基础-003Go 语言结构
01GO基础-004Go 语言基础语法1
01GO基础-004Go 语言基础语法2
01GO基础-004Go 语言基础语法3
01GO基础-005Go 语言数据类型
01GO基础-006Go 语言变量
01GO基础-007Go 语言常量
01GO基础-008Go 语言运算符
01GO基础-009条件语句
01GO基础-010循环语句
01GO基础-011函数
01GO基础-012变量作用域
01GO基础-013数组
01GO基础-014指针
01GO基础-015结构体
01GO基础-016切片
01GO基础-017范围(Range)
01GO基础-018Map
01GO基础-019递归函数
01GO基础-020类型转换
01GO基础-021接口
01GO基础-022异常处理
01GO基础-023并发
01GO基础-024strings
01GO基础-025可变参数
01GO基础-026接口2
01GO基础-027异常处理2
01GO基础-028sync包详解
01GO基础-029Context
02GO进阶001包
02GO进阶002init()函数
02GO进阶003包的注意点
02GO进阶003使用go module导入本地包
02GO进阶004 time包
02GO进阶005 file操作
02GO进阶006 io操作
02GO进阶007 os包(文件 I/O、文件属性、目录与链接、创建和移除链接)
02GO进阶008复制文件
02GO进阶009断点续传
02GO进阶010 bufio包
02GO进阶011ioutil包
02GO进阶012遍历文件夹
02GO进阶013并发编程介绍
02GO进阶014Goroutine协程
02GO进阶015 GPM
02GO进阶016 runtime包
02GO进阶017 Channel
02GO进阶018 Goroutine池
02GO进阶019 定时器
02GO进阶020 select
02GO进阶021并发安全和锁
02GO进阶022sync
02GO进阶023原子操作
02GO进阶024 GMP原理与调度
02GO进阶025爬虫小案例
02GO进阶026 面向对象-匿名字段
02GO进阶026 面向对象-接口
02GO进阶027网络编程-互联网协议介绍
02GO进阶027网络编程-socket
02GO进阶027网络编程-http编程
02GO进阶027网络编程-websocket编程
02GO进阶028数据操作-MYSQL
02GO进阶028数据操作-REDIS
02GO进阶028数据操作-RTCD
02GO进阶028数据操作-ZOOKEEPER
02GO进阶028数据操作-KAFKA
02GO进阶028数据操作-RabbitMQ
02GO进阶028数据操作-ElasticSearch
02GO进阶028数据操作-NSQ
02GO进阶028数据操作-memcached
02GO进阶028数据操作-GORM
02GO进阶029beego框架-安装
02GO进阶029beego框架-快速入门
02GO进阶029beego框架-MVC架构介绍-controller设计-参数配置
02GO进阶029beego框架-MVC架构介绍-controller设计-路由设置
02GO进阶029beego框架-MVC架构介绍-controller设计-控制器函数
02GO进阶029beego框架-MVC架构介绍-controller设计-XSRF过滤
02GO进阶029beego框架-MVC架构介绍-controller设计-请求数据处理
02GO进阶029beego框架-MVC架构介绍-controller设计-Session控制
02GO进阶029beego框架-MVC架构介绍-controller设计-过滤器
02GO进阶029beego框架-MVC架构介绍-controller设计-Flash数据
02GO进阶029beego框架-MVC架构介绍-controller设计-URL构建
02GO进阶029beego框架-MVC架构介绍-controller设计-多种格式数据输出
02GO进阶029beego框架-MVC架构介绍-controller设计-表单数据验证
02GO进阶029beego框架-MVC架构介绍-controller设计-错误处理
02GO进阶029beego框架-MVC架构介绍-controller设计-日志处理
02GO进阶029beego框架-MVC架构介绍-model设计-概述
02GO进阶029beego框架-MVC架构介绍-model设计-CRUD操作
02GO进阶029beego框架-MVC架构介绍-model设计-高级查询
02GO进阶029beego框架-MVC架构介绍-model设计-原生SQL查询
02GO进阶029beego框架-MVC架构介绍-model设计-构造查询
02GO进阶029beego框架-MVC架构介绍-model设计-事务处理
02GO进阶029beego框架-MVC架构介绍-model设计-模型定义
02GO进阶029beego框架-MVC架构介绍-model设计-命令模式
02GO进阶029beego框架-MVC架构介绍-model设计-测试用例
02GO进阶029beego框架-MVC架构介绍-view设计-beego 模板语法指南
02GO进阶029beego框架-MVC架构介绍-view设计-模板处理
02GO进阶029beego框架-MVC架构介绍-view设计-其他
本文档使用 MrDoc 发布
-
+
首页
02GO进阶008复制文件
在io包中主要是操作流的一些方法,今天主要学习一下copy。就是把一个文件复制到另一个目录下。 它的原理就是通过程序,从源文件读取文件中的数据,在写出到目标文件里。  ## 一、方法一:io包下的Read()和Write()方法实现 我们可以通过io包下的Read()和Write()方法,边读边写,就能够实现文件的复制。这个方法是按块读取文件,块的大小也会影响到程序的性能。 ```go } /* 该函数的功能:实现文件的拷贝,返回值是拷贝的总数量(字节),错误 */ func copyFile1(srcFile,destFile string)(int,error){ file1,err:=os.Open(srcFile) if err != nil{ return 0,err } file2,err:=os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm) if err !=nil{ return 0,err } defer file1.Close() defer file2.Close() //拷贝数据 bs := make([]byte,1024,1024) n :=-1//读取的数据量 total := 0 for { n,err = file1.Read(bs) if err == io.EOF || n == 0{ fmt.Println("拷贝完毕。。") break }else if err !=nil{ fmt.Println("报错了。。。") return total,err } total += n file2.Write(bs[:n]) } return total,nil } ``` ## 二、方法二:io包下的Copy()方法实现 我们也可以直接使用io包下的Copy()方法。 示例代码如下: ```go func copyFile2(srcFile, destFile string)(int64,error){ file1,err:=os.Open(srcFile) if err != nil{ return 0,err } file2,err:=os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm) if err !=nil{ return 0,err } defer file1.Close() defer file2.Close() return io.Copy(file2,file1) } ``` ### 扩展内容: 在io包(golang 版本 1.12)中,不止提供了Copy()方法,还有另外2个公开的copy方法:CopyN(),CopyBuffer()。 ```go Copy(dst,src) 为复制src 全部到 dst 中。 CopyN(dst,src,n) 为复制src 中 n 个字节到 dst。 CopyBuffer(dst,src,buf)为指定一个buf缓存区,以这个大小完全复制。 ``` 他们的关系如下:  从图可以看出,无论是哪个copy方法最终都是由copyBuffer()这个私有方法实现的。 ```go func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { // If the reader has a WriteTo method, use it to do the copy. // Avoids an allocation and a copy. if wt, ok := src.(WriterTo); ok { return wt.WriteTo(dst) } // Similarly, if the writer has a ReadFrom method, use it to do the copy. if rt, ok := dst.(ReaderFrom); ok { return rt.ReadFrom(src) } if buf == nil { size := 32 * 1024 if l, ok := src.(*LimitedReader); ok && int64(size) > l.N { if l.N < 1 { size = 1 } else { size = int(l.N) } } buf = make([]byte, size) } for { nr, er := src.Read(buf) if nr > 0 { nw, ew := dst.Write(buf[0:nr]) if nw > 0 { written += int64(nw) } if ew != nil { err = ew break } if nr != nw { err = ErrShortWrite break } } if er != nil { if er != EOF { err = er } break } } return written, err } ``` 从这部分代码可以看出,复制主要分为3种。 1.如果被复制的Reader(src)会尝试能否断言成writerTo,如果可以则直接调用下面的writerTo方法 2.如果 Writer(dst) 会尝试能否断言成ReadFrom ,如果可以则直接调用下面的readfrom方法 3.如果都木有实现,则调用底层read实现复制。 其中,有这么一段代码: ```go if buf == nil { size := 32 * 1024 if l, ok := src.(*LimitedReader); ok && int64(size) > l.N { if l.N < 1 { size = 1 } else { size = int(l.N) } } buf = make([]byte, size) } ``` 这部分主要是实现了对Copy和CopyN的处理。通过上面的调用关系图,我们看出CopyN在调用后,会把Reader转成LimiteReader。 区别是如果Copy,直接建立一个缓存区默认大小为 32* 1024 的buf,如果是CopyN 会先判断 要复制的字节数,如果小于默认大小,会创建一个等于要复制字节数的buf。 ## 三、方法三:ioutil包 第三种方法是使用ioutil包中的 `ioutil.WriteFile()`和 `ioutil.ReadFile()`,但由于使用一次性读取文件,再一次性写入文件的方式,所以该方法不适用于大文件,容易内存溢出。 示例代码: ```go func copyFile3(srcFile, destFile string)(int,error){ input, err := ioutil.ReadFile(srcFile) if err != nil { fmt.Println(err) return 0,err } err = ioutil.WriteFile(destFile, input, 0644) if err != nil { fmt.Println("操作失败:", destFile) fmt.Println(err) return 0,err } return len(input),nil } ``` ## 四、总结 最后,我们来测试一下这3种拷贝需要花费时间,拷贝的文件都是一样的一个mp4文件(400M),  代码: ```go func main() { /* 复制文件: */ //srcFile := "/home/ruby/文档/pro/aa.txt" //destFile := "/home/ruby/文档/aa.txt" srcFile :="/Users/ruby/Documents/pro/a/001_小程序入门.mp4" destFile:="001_小程序入门.mp4" total,err:=copyFile1(srcFile,destFile) fmt.Println(err) fmt.Println(total) } ``` 第一种:io包下Read()和Write()直接读写:我们自己创建读取数据的切片的大小,直接影响性能。 ```go localhost:l_file ruby$ time go run demo05_copy.go 拷贝完毕。。 <nil> 401386819 real 0m7.911s user 0m2.900s sys 0m7.661s ``` 第二种:io包下Copy()方法: ```go localhost:l_file ruby$ time go run demo05_copy.go <nil> 401386819 real 0m1.594s user 0m0.533s sys 0m1.136s ``` 第三种:ioutil包 ```go localhost:l_file ruby$ time go run demo05_copy.go <nil> 401386819 real 0m1.515s user 0m0.339s sys 0m0.625s ``` 运行结果:  这3种方式,在性能上,不管是还是io.Copy()还是ioutil包,性能都是还不错的。 ```go package main import ( "os" "io" "fmt" "io/ioutil" ) func main() { /* 拷贝文件: */ srcFile := "/Users/ruby/Documents/pro/a/guliang.jpeg" destFile := "guliang3.jpeg" //total,err := CopyFile1(srcFile,destFile) //total,err := CopyFile2(srcFile,destFile) total,err := CopyFile3(srcFile,destFile) fmt.Println(total,err) } func CopyFile3(srcFile,destFile string)(int,error){ bs,err := ioutil.ReadFile(srcFile) if err != nil{ return 0,err } err = ioutil.WriteFile(destFile,bs,0777) if err != nil{ return 0,err } return len(bs),nil } func CopyFile2(srcFile,destFile string)(int64,error){ file1,err := os.Open(srcFile) if err != nil{ return 0,err } file2,err := os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm) if err != nil{ return 0,err } defer file1.Close() defer file2.Close() return io.Copy(file2,file1) } //该函数:用于通过io操作实现文件的拷贝,返回值是拷贝的总数量(字节),错误 func CopyFile1(srcFile,destFile string)(int,error){ file1,err :=os.Open(srcFile) if err != nil{ return 0,err } file2,err := os.OpenFile(destFile,os.O_WRONLY|os.O_CREATE,os.ModePerm) if err != nil{ return 0,err } defer file1.Close() defer file2.Close() //读写 bs := make([]byte,1024,1024) n := -1 //读取的数据量 total := 0 for{ n,err = file1.Read(bs) if err == io.EOF || n==0{ fmt.Println("拷贝完毕。。") break }else if err != nil{ fmt.Println("报错了。。") return total,err } total += n file2.Write(bs[:n]) } return total,nil } ```
admin
2024年12月24日 13:53
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码