Bruce Blog Bruce Blog
首页
  • CentOS
  • Ubuntu-Debian
  • 系统网络
  • 系统辅助工具
  • MySQL
  • Redis
  • Mongodb
  • Docker基础
  • Container基础
  • Kubernetes

    • Kubernetes基础
    • Kubernetes辅助
  • Container-Network
  • Jenkins
  • Gitlab
  • ArgoCD
  • Ansible
  • Terraform
  • AWS
  • MQ
  • NGINX
  • JumpServer
  • 基础
  • 函数模块
  • 框架
  • 基础

    • Golang环境
    • 语法
    • 数据类型与运算符
    • 分支语句
    • 循环语句
    • 数组
    • 切片
    • Map
    • String
    • 函数
    • 包的管理
    • 指针
    • 结构体
    • Go语言中的OOP
    • 方法和接口
    • 错误处理
  • Go进阶

    • Go进阶
  • Go框架

    • Go框架
  • Golang辅助

    • Golang辅助
  • CSS
  • HTML
  • JavaScript
  • 前端辅助
  • 常用命令
  • 性能监控工具
  • Windows下Docker使用
  • 日常学习
  • 其他导航

Bruce Tao

运维界的该溜子
首页
  • CentOS
  • Ubuntu-Debian
  • 系统网络
  • 系统辅助工具
  • MySQL
  • Redis
  • Mongodb
  • Docker基础
  • Container基础
  • Kubernetes

    • Kubernetes基础
    • Kubernetes辅助
  • Container-Network
  • Jenkins
  • Gitlab
  • ArgoCD
  • Ansible
  • Terraform
  • AWS
  • MQ
  • NGINX
  • JumpServer
  • 基础
  • 函数模块
  • 框架
  • 基础

    • Golang环境
    • 语法
    • 数据类型与运算符
    • 分支语句
    • 循环语句
    • 数组
    • 切片
    • Map
    • String
    • 函数
    • 包的管理
    • 指针
    • 结构体
    • Go语言中的OOP
    • 方法和接口
    • 错误处理
  • Go进阶

    • Go进阶
  • Go框架

    • Go框架
  • Golang辅助

    • Golang辅助
  • CSS
  • HTML
  • JavaScript
  • 前端辅助
  • 常用命令
  • 性能监控工具
  • Windows下Docker使用
  • 日常学习
  • 其他导航
  • 基础

  • Go进阶

    • Go进阶
    • File操作
    • IO操作
    • 文件复制
      • 断点续传
      • bufio包
      • ioutil包
      • 遍历目录
      • 并发性Concurrency概念
      • Goroutine初识
      • Goroutine并发模型
      • Runtime包
      • 临界资源安全问题
      • sync包WaitGroup
      • 互斥锁
      • 读写锁
      • Channel通道
      • 关闭通道和通道上范围循环
      • 缓冲通道
      • 定向通道
      • time包中的通道相关函数
      • select语句
      • CSP并发模型
      • Go语言反射(一)
      • Go语言反射(二)
    • Go框架

    • Golang辅助

    • Golang
    • Go进阶
    Bruce
    2022-12-03
    目录

    文件复制

    # 文件复制

    image-20221030162252941

    # 一、io包下的Read()和Write()方法实现

    • 可以通过io包下的Read()和Write方法,边读边写,就能够实现文件的复制.这个方法是按块读取文件,块的大小也会影响到程序的性能.
    /*
    该函数的功能: 实现文件的拷贝,返回值时拷贝的总数量(字节),错误
    */
    
    func copyFile(srcFile,destFile string)(int,error){
        file1,err := os.Open(srcFile)
        if err != niln {
            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
        
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35

    # 二、io包下的Copy()方法实现

    • 也可以直接使用io包下的Copy()方法
    • io包Copy地址: https://golang.google.cn/pkg/io/#Copy
    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)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 扩展内容
    • 在io包中,不止提供了Copy()方法,还有另外2个公开的copy方法: CopyN()、CopyBuffer()
    Copy (dst,src) 为赋值src 全部到 dst中
    
    CopyN(dst,src,n) 为赋值src 中 n 个字节到 dst.
    
    CopyBuffer(dst,src,buf) 为指定一个`buf`缓存区,以这个大小完全复制.
    
    1
    2
    3
    4
    5

    他们的关系如下:

    image-20221030175347421

    func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
    	written, err = Copy(dst, LimitReader(src, n))
    	if written == n {
    		return n, nil
    	}
    	if written < n && err == nil {
    		// src stopped early; must have been EOF.
    		err = EOF
    	}
    	return
    }
    
    // Copy copies from src to dst until either EOF is reached
    // on src or an error occurs. It returns the number of bytes
    // copied and the first error encountered while copying, if any.
    //
    // A successful Copy returns err == nil, not err == EOF.
    // Because Copy is defined to read from src until EOF, it does
    // not treat an EOF from Read as an error to be reported.
    //
    // If src implements the WriterTo interface,
    // the copy is implemented by calling src.WriteTo(dst).
    // Otherwise, if dst implements the ReaderFrom interface,
    // the copy is implemented by calling dst.ReadFrom(src).
    func Copy(dst Writer, src Reader) (written int64, err error) {
    	return copyBuffer(dst, src, nil)
    }
    
    // CopyBuffer is identical to Copy except that it stages through the
    // provided buffer (if one is required) rather than allocating a
    // temporary one. If buf is nil, one is allocated; otherwise if it has
    // zero length, CopyBuffer panics.
    //
    // If either src implements WriterTo or dst implements ReaderFrom,
    // buf will not be used to perform the copy.
    func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
    	if buf != nil && len(buf) == 0 {
    		panic("empty buffer in CopyBuffer")
    	}
    	return copyBuffer(dst, src, buf)
    }
    
    // copyBuffer is the actual implementation of Copy and CopyBuffer.
    // if buf is nil, one is allocated.
    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 || nr < nw {
    				nw = 0
    				if ew == nil {
    					ew = errInvalidWrite
    				}
    			}
    			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
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94

    # 三、ioutil包

    • 第三种方法是使用ioutil包中的ioutil.WriteFile()和ioutil.ReadFile(),但由于使用一次性读取文件,再一次性写入文件的方式,所以该方法不使用与大文件,容易内存溢出
    • ioutil包地址: https://golang.google.cn/pkg/io/ioutil/
    func copyFile3(srcFile,dstFile string)(int,error){
        input,err := ioutil.ReadFile(srcFile)
        if err != nil {
            fmt.Println(err)
            return 0,err
        }
        
        err = ioutil.WriteFile(dstFile,input,0644)
        if err != nil {
            fmt.Println("操作失败: ",dstFile)
            fmt.Println(err)
        }
        
        return len(input),nil
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    # 四、总结

    • 测试3种方法,拷贝大文件(例如MP4的视频文件)
    func main() {
        /*
        复制文件:
        */
        srcFile := "E:\Github-Project\GO-Project\src\upgrade-qf\03.file\01.Golang入门视频.mp4"
        dstFile := "E:\Github-Project\GO-Project\src\upgrade-qf\03.file\02.Golang入门视频.mp4"
        total,err := copyFile1(srcFile,dstFile)
        fmt.Println(err)
        fmt.Println(total)
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 方法一
    • io包下的Read()和Write()直接读写(程序中创建的读取数据切片的大小,直接影响性能)
    $ time go run file-copy01.go
    拷贝完毕...
    <nil>
    401386819
    
    real 0m7.911s
    user 0m2.900s
    sys 0m7.661s
    
    1
    2
    3
    4
    5
    6
    7
    8
    # 方法二
    • io包下Copy()方法
    $ time go run file-copy02.go
    <nil>
    401386819
    
    real 0m1.594s
    user 0m0.533s
    sys 0m1.136s
    
    1
    2
    3
    4
    5
    6
    7
    # 方法三
    • ioutil包
    $ time go run file-copy03.go
    <nil>
    401386819
    
    real 0m1.515s
    user 0m0.339s
    sys 0m0.625s
    
    1
    2
    3
    4
    5
    6
    7
    # 备注:
    • 在性能上,不管是io.Copy()还是ioutil包,性能都还是不错的.
    上次更新: 2024/04/09, 16:48:42
    IO操作
    断点续传

    ← IO操作 断点续传→

    最近更新
    01
    AWS NAT-NetWork-Firwalld配置(一)
    04-09
    02
    AWS NAT-NetWork-Firwalld配置(二)
    04-09
    03
    kubernetes部署minio对象存储
    01-18
    更多文章>
    Theme by Vdoing | Copyright © 2019-2024 Bruce Tao Blog Space | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式