手动实现

思路就是:先创建一个切片 然后通过Read方法操作源文件把数据读到切片中,然后再通过Write方法把切片里面的数据写到目标文件里面。

具体怎么使用呢?
举个例子:
如果我有一个文件test.txt,然后我想要把这个文件复制到destination.txt,就可以这样写:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    // 拷贝文件
    srcFile := "/Users/liberhome/GolandProjects/awesomeProject/I-package/April28_Go_io/test.txt"
    destFile := "destination.txt"
    total, err := copyFile1(srcFile, destFile) //函数的具体实现在下面
    fmt.Println(total, err)
}

func copyFile1(srcFile, destFile string) (int, error) { //返回值是完成拷贝数据的字节数&err
    file1, err := os.Open(srcFile) //这里读 所以用open就足够了
    if err != nil {
        return 0, err
    }
    file2, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE, os.ModePerm) //这里要写文件 所以需要用到OpenFile
    if err != nil {
        return 0, err
    }
    // 关闭文件
    defer file1.Close()
    defer file2.Close()
    //读写文件
    bs := make([]byte, 1024, 1024)//用make创造切片
    n := -1    //每次读取的字节数
    total := 0 //读取的总数量
    for {
        n, err = file1.Read(bs)
        if err == io.EOF || n == 0 {
            fmt.Println("copy is complete")
            break
        } else if err != nil {
            fmt.Println("error happen")
            return total, err
        }
        total += n
        file2.Write(bs[:n])
    }
    return total, nil
}

当然了,这段代码不仅可以复制txt文件,其他类型,比如png什么的都不在话下,毕竟是通过字节传递的。

以上就是手动实现copy,当然这太麻烦了,实际工作中很少这么用,之所以放在这里,是为了更深层的理解他的实现原理,万变不离其宗,这就是宗。

接下来介绍一下实际常用的

库函数实现

io.Copy()方法实现

func Copy(dst Writer, src Reader) (written int64, err error)  {}

这个函数需要两个参数,第一个是向外写的目的对象,第二个是读进来的对象;返回值有两个,一个是复制的总数据量,一个是错误。需要注意的是,如果成功拷贝,返回值是nil 不是EOF。

具体怎么使用呢?
还是复制上面例子中的文件,代码如下

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    // 拷贝文件
    srcFile := "/Users/liberhome/GolandProjects/awesomeProject/I-package/April28_Go_io/test.txt"
    destFile := "destination2.txt"
    //total, err := copyFile1(srcFile, destFile) //函数的具体实现在下面
    total, err := copyFile2(srcFile, destFile) //函数的具体实现在下面
    fmt.Println(total, err)
}

func copyFile1(srcFile, destFile string) (int, error) { //返回值是完成拷贝数据的字节数&err
    file1, err := os.Open(srcFile) //这里读 所以用open就足够了
    if err != nil {
        return 0, err
    }
    file2, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE, os.ModePerm) //这里要写文件 所以需要用到OpenFile
    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("copy is complete")
            break
        } else if err != nil {
            fmt.Println("error happen")
            return total, err
        }
        total += n
        file2.Write(bs[:n])
    }
    return total, nil
}

func copyFile2(srcFile, destFile string) (int64, error) {
    file1, err := os.Open(srcFile) //这里读 所以用open就足够了
    if err != nil {
        return 0, err
    }
    file2, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE, os.ModePerm) //这里要写文件 所以需要用到OpenFile
    if err != nil {
        return 0, err
    }
    // 关闭文件
    defer file1.Close()
    defer file2.Close()
    //就是在这里直接调用库函数~
    return io.Copy(file2, file1)
}

除Copy之外,io包还提供了CopyN(dst, src, n)【复制src中n个字节到dst】以及copyBuffer(dst, src, buf)【指定一个buf区域,以后都已这个buf大小完全复制】,
他们之间的底层实现逻辑如下:
copyBuffer与Copy、CopyN之间的关系
所以,本质上都是基于copyBuffer,当不传buf的时候默认缓冲区buf是32*1024。

ioutil包的方法实现

但是由于ioutil包中的writeFile和readFile方法都是一次性读取再一次性写入,对大文件并不友好,这里不再展开。