Go I/O操作 实现断点续传

164 阅读3分钟

Seeker

设置光标的位置, 读写文件

上图中第一个参数 offset 为偏移量, 第二个参数 whence 为如何设置 (当前光标的位置)

理解参数设置, 如何定位想要的位置 ?

    1. 定下当前光标所在位置
    1. 确定偏移量

package main

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

func main() {
	// 读取文件
	file, _ := os.OpenFile("D:\Environment\GoWorks\src\studygo\basic_learn02\a.txt",
		os.O_RDWR, os.ModePerm)
	defer file.Close()
	file.Seek(2, io.SeekStart) // +x 光标往后 -x 光标往前
	buf := []byte{0}
	file.Read(buf)
	fmt.Println(string(buf))
	file.Seek(2, io.SeekCurrent) // 移动光标
	file.Read(buf)               // 读光标后的字符
	fmt.Println(string(buf))
	// 在结尾追加内容
	file.Seek(0, io.SeekEnd)
	file.WriteString("hello, sam")
}

断点续传

如何实现断点续传?

这里采用的是分片传, 记录其间传的文件光标位置, 支持恢复上传

package main

import (
	"fmt"
	"io"
	"os"
	"strconv"
	"time"
)

// 实现简易的断点续传
func main() {
	// 传输数据源文件
	srcFile := "D:\OneDrive_files\OneDrive - ebl6\桌面\client\1.jpg"
	now := time.Now().Format("150405")
	//fmt.Println(now)
	// 传输的目的地址
	destFile := "D:\Environment\GoWorks\src\studygo\basic_learn02\server\" + now + ".jpg"
	// 创建临时文件 (能够持续化存储, 这种直接将偏移值放在txt里, 然后读取 效率高)
	tempFile := "D:\Environment\GoWorks\src\studygo\basic_learn02\temp.txt"

	file, _ := os.Open(srcFile)                                           // 源文件
	file1, _ := os.OpenFile(destFile, os.O_CREATE|os.O_RDWR, os.ModePerm) // 目标文件
	file2, _ := os.OpenFile(tempFile, os.O_CREATE|os.O_RDWR, os.ModePerm) // 临时文件
	defer file.Close()
	defer file1.Close()

	file2.Seek(0, io.SeekStart)
	buf := make([]byte, 1024, 1024)
	n, _ := file2.Read(buf) // 找光标位置
	// 把读出的内容转换成 数字 (为下步寻找传输文件的光标做准备)
	conStr := string(buf[:n])
	i, _ := strconv.ParseInt(conStr, 10, 64) // 十进制 64位
	fmt.Println(i)
	// 设置偏移量 (读写文件中的光标要保持一致)
	file.Seek(i, io.SeekStart) // 可以做到断点续传
	file1.Seek(i, io.SeekStart)

	// 开始传输
	bufData := make([]byte, 1024, 1024)
	// 记录读取了多少个字符
	total := int(i)
	for {
		//fmt.Println(total)
		read, err := file.Read(bufData)
		if err == io.EOF { // 源文件数据已读完
			fmt.Println("文件传输完毕")
			file2.Close()
			os.Remove(tempFile)
			break
		}
		// 向目标文件写入数据
		write, _ := file1.Write(bufData[:read])
		//fmt.Println(write)
		// 若读到一半就出意外了 如何处理
		// 1. 重新下载 2. 上次记录的total大小/bufSize(缓冲区大小) + write 可以找到出意外没传输完的光标位置
		// 将写入的字符数记录在 total中 -> 记录断点续传的进度
		total += write
		// 存放临时记录
		file2.Seek(0, io.SeekStart)
		file2.WriteString(strconv.Itoa(total)) // 覆盖
		//if total > 10240 {
		//	panic("断电了..")
		//}

		//time.Sleep(time.Second * 2)
	}
}

遍历文件夹

package main

import (
	"fmt"
	"log"
	"os"
)

// 遍历文件夹
// 递归实现
// 是文件的话就读名字
// 是文件夹的话就进入 .isDir() 来判断
func main() {
	// 获取目录信息
	Michael_tree("D:\Environment\GoWorks\src\studygo\basic_learn02", 0)
}

func Michael_tree(dir string, level int) { // 第一个参数为路径 第二个参数记录目录层数
	// 层级
	tree_level := "|--"
	for i := 0; i < level; i++ {
		tree_level += "|--"
	}
	infos, err := os.ReadDir(dir) // 返回的是切片 []DirEntry -> 多个文件信息
	if err != nil {
		log.Println(err) // log 日志输出
	}
	for _, file := range infos { // index, value
		// 文件全路径
		filename := dir + "\" + file.Name()
		//fmt.Print(tree_level) // 加层次~
		fmt.Print(tree_level + file.Name())
		fmt.Println()
		if file.IsDir() {
			Michael_tree(filename, level+1)
		}
	}
}

bufio

Go 自带的IO操作包

使用bufio 进行文件读取, 以及读取键盘输入

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
)

func main() {
	file, err := os.Open("这里换成待读入的文件名(绝对路径)") // 默认是只读模式打开
	if err != nil {
		log.Println(err)
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	//buf := make([]byte, 1024, 1024)
	//n, err := reader.Read(buf)
	//if err != io.EOF {
	//	log.Println(err)
	//}
	//fmt.Println("读取到了多少个字节: ", n)
	//fmt.Println("读取数据", string(buf[:n]))
	line, _, _ := reader.ReadLine()
	fmt.Println(string(line)) // 读一行数据
	// 读取键盘输入 (I/O读入)
	newReader := bufio.NewReader(os.Stdin)
	readString, _ := newReader.ReadString('\n') // 读取键盘输入的信息(以'\n'回车符为分隔)
	fmt.Println("读取键盘信息: ", readString)
}

使用bufio 对文件进行写入操作, bufio 会先将输入文件的内容放在缓冲区中, 需要通过手动刷新缓冲区, 这样内容才真正写到文件里 .

package main

import (
	"bufio"
	"fmt"
	"os"
)

// bufio 写入文件
func main() {
	file, _ := os.OpenFile("这里换成待操作的文件名(绝对路径)",
		os.O_WRONLY|os.O_WRONLY,
		os.ModePerm) // 最后一个参数是权限
	defer file.Close()

	// bufio 写入
	bufioWriter := bufio.NewWriter(file)
	writeNum, _ := bufioWriter.WriteString("HELLO?")
	fmt.Println("写入的字符数:", writeNum)
	// 发现文件还是没有写入
	// 是因为写入的内容还在缓冲区, 我们需要 flush(手动刷新)缓冲区 这样才能写入到文件中~
	bufioWriter.Flush() // 默认是从从头开始写 (光标)
}