之前使用beego的http库的时候,有的情况需要下载文件。beego是能实现,但就是有点问题:不支持回调,没法显示下载速度,这在日常开发中是不可忍受的。
看了下beego的实现主要是使用了io.copy函数,于是就深入的看了下实现原理,发现挺简单的,于是就根据io.copy原理实现的一个简单的下载器
//定义要下载的文件
var durl = "https://dl.google.com/go/go1.10.3.darwin-amd64.pkg";
//解析url
uri, err := url.ParseRequestURI(durl)
if err != nil {
panic("网址错误")
}
正常的一个流程先看看Url有没有错误
filename := path.Base(uri.Path)
log.Println("[*] Filename " + filename)
使用path.Base取出url的文件名称,这里有个可能会出现bug的地方:如果这个网址是302跳转过去的话是没法取出跳转过后的文件名称。
取文件名称这步可以再发送http请求的时候 client.CheckRedirect定义一个函数取出文件名次即可。
client := http.DefaultClient;
client.Timeout = time.Second * 60 //设置超时时间
resp, err := client.Get(durl)
创建一个httpClient,另外这个客户端的Timeout是设置读取数据的时候多少时间算超时。
我这里比较推崇用Do方法传递一个Reqeust过去,应为有的url需要校验http头之类的,你问我为啥不用一个字懒。
raw := resp.Body
defer raw.Close()
reader := bufio.NewReaderSize(raw, 1024*32);
其实说实话不太清楚bufio能不能真正的加快htpp读取速度,没有做过跟不用bufio的对比。但是为了心理上的安心就加上去了
file, err := os.Create(filename)
if err != nil {
panic(err)
}
writer := bufio.NewWriter(file)
同上也不太确定bufio能对文件写入增加多少速度
buff := make([]byte, 32*1024)
written := 0
go func() {
for {
nr, er := reader.Read(buff)
if nr > 0 {
nw, ew := writer.Write(buff[0:nr])
if nw > 0 {
written += nw
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
if err != nil {
panic(err)
}
}()
这个源码是我直接复制io.copyBuffer函数的只是做了一些简单的修改理解大概意思就行
//间隔时间
spaceTime := time.Second * 1
//定时器
ticker := time.NewTicker(spaceTime)
//上次读取数据大小
lastWtn := 0
stop := false
for {
select {
case <-ticker.C:
//这次读取的文件大小-上次读取的数据大小=速度
speed := written - lastWtn
log.Printf("[*] Speed %s / %s n", bytesToSize(speed), spaceTime.String())
if written-lastWtn == 0 {
ticker.Stop()
stop = true
break
}
lastWtn = written
}
if stop {
break
}
}









