当我们用百度网盘下载或者迅雷下载时,某一时刻下到一半不想下了,按了暂停,关了程序,关了机,第二天打开百度网盘,继续下昨天的东西,发现,是从昨天暂停的地方开始继续下载的,这种体验就很棒。不然,像百度云盘不充会员只有100k左右的下载速度,一下午加一晚上好不容易下了一半,睡觉去了,第二天打开电脑接着下,发现还要重新开始,看着这老牛的速度,崩溃了。这就体现了断点续传的妙用了。
我们用client模拟百度云盘,迅雷等app,用server模拟申请下载方。
1、整体思路
对于client端上传文件,需要先知道断点在哪里,也就是说需要从服务器读一个整型,这个数代表下载一方上次读到哪了,然后文件光标偏移到这个地方,读文件,写入连接,完成上传文件。
2、代码实现
⚪获取连接
conn, err := net.Dial("tcp","localhost:8080")
if err != nil{
fmt.Println("net.Dial err", err)
return
}
defer conn.Close()
⚪告诉下载方开始上传
defer conn.Close()
⚪读取下载方传来的上次下载的位置
buf := make([]byte, 10,10)
n,err:=conn.Read(buf)
if err !=nil{
fmt.Println("conn.Read err", err)
return
}
⚪光标偏移到上次下载的位置
pos,_ := strconv.Atoi(string(buf[:n]))
file.Seek(int64(pos),0)
⚪循环读取读取文件直到结束
for{
buf:=make([]byte,10,10)
n,err:=file.Read(buf)
if err==io.EOF{
conn.Write([]byte("<--end"))
fmt.Println("传输完成")
return
}else{
conn.Write(buf[:n])
}
}
这里模拟的再真实一点,在file.read之前可以conn.Read一下,如果下载方传了一个”暂停“,上传一方直接break,这也防止了异常退出带来的不可预料的错误
1、对于server端下载文件,在建立连接之后,需要先发送文件的字节长度,代表上次读到哪了,然后读连接,写入文件,完成下载文件
2.代码实现
⚪首先解释一下如何获得上次下载的位置
就是看看本地文件有多少个字节。用到os.Stat函数,返回一个文件信息,文件信息里有文件的大小,如果没有该文件返回一个os.IsNotExist,文件不存在大小肯定是0,
func Stat(name string) (fi FileInfo, err error)
下面编程
func GetPos(filepath string)int64{
file, err := os.Stat(filepath)
if err != nil{
if os.IsNotExist(err){
return 0
}
}
return file.Size()
}
⚪监听端口,得到连接
listener,err := net.Listen("tcp", "localhost:8080")
if err != nil{
fmt.Println("net.Listen err", err)
return
}
conn,err := listener.Accept()
if err != nil{
fmt.Println("listener.Accept err", err)
return
}
⚪得到pos,如果大小为零,就创建一个文件,否则就以追加写的方式打开,得到可用于i/o的文件句柄。
filepath := "G:\\BaiduNetdiskDownload\\CSDN算法\\小课资料\\day10\\Get作业.txt"
pos := int(GetPos(filepath))
intstr:= strconv.Itoa(pos)
var file *os.File
if pos==0{
file,_ = os.Create(filepath)
}else{
file,_ = os.OpenFile(filepath,os.O_APPEND,0777)
}
⚪循环读连接,如果读到“start–>”,表示上传方做好了准备,这时要先发一个pos,告诉上传方上次读到哪了了,然后从链接继续读,写入本地文件,知道读到“end"关闭连接,关闭文件句柄
for{
buf := make([]byte,10,10)
n,err := conn.Read(buf)
if err != nil{
if err == io.EOF{
fmt.Println("连接断开了")
return
}
fmt.Println("read err", err)
return
}
if n != 0{
if string(buf[:n])=="start-->"{
conn.Write([]byte(intstr))
}else if string(buf[:n])=="<--end"{
fmt.Println("客户端传输完成")
file.Close()
}else{
fmt.Println("正在写入文件:", string(buf[:n]))
_,err:=file.Write(buf[:n])
if err != nil{
fmt.Println("写入",err)
}
}
}
}