Golang 使用http Client下载文件的实现方法
之前使用beego的http库的时候,有的情况需要下载文件。beego是能实现,但就是有点问题:不支持回调,没法显示下载速度,这在日常开发中是不可忍受的。
看了下beego的实现主要是使用了io.copy函数,于是就深入的看了下实现原理,发现挺简单的,于是就根据io.copy原理实现的一个简单的下载器
//定义要下载的文件
vardurl="https://dl.google.com/go/go1.10.3.darwin-amd64.pkg";
//解析url
uri,err:=url.ParseRequestURI(durl)
iferr!=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 deferraw.Close() reader:=bufio.NewReaderSize(raw,1024*32);
其实说实话不太清楚bufio能不能真正的加快htpp读取速度,没有做过跟不用bufio的对比。但是为了心理上的安心就加上去了
file,err:=os.Create(filename)
iferr!=nil{
panic(err)
}
writer:=bufio.NewWriter(file)
同上也不太确定bufio能对文件写入增加多少速度
buff:=make([]byte,32*1024)
written:=0
gofunc(){
for{
nr,er:=reader.Read(buff)
ifnr>0{
nw,ew:=writer.Write(buff[0:nr])
ifnw>0{
written+=nw
}
ifew!=nil{
err=ew
break
}
ifnr!=nw{
err=io.ErrShortWrite
break
}
}
ifer!=nil{
ifer!=io.EOF{
err=er
}
break
}
}
iferr!=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())
ifwritten-lastWtn==0{
ticker.Stop()
stop=true
break
}
lastWtn=written
}
ifstop{
break
}
}
这段代码堵塞住程序,然后定时器按照间隔时间来计算速度。这里可能会出现bug的地方就是在间隔时间内没有读取到数据的时候回直接跳出循环。现在这个时间点非常困,写个blog已经困的要死了以后有空把这个bug解决了吧
funcbytesToSize(lengthint)string{
vark=1024//or1024
varsizes=[]string{"Bytes","KB","MB","GB","TB"}
iflength==0{
return"0Bytes"
}
i:=math.Floor(math.Log(float64(length))/math.Log(float64(k)))
r:=float64(length)/math.Pow(float64(k),i)
returnstrconv.FormatFloat(r,'f',3,64)+""+sizes[int(i)]
}
这个函数是我从我个人的php项目中转换过来的
2018/08/1700:24:50[*]Filenamego1.10.3.darwin-amd64.pkg 2018/08/1700:24:51[*]Speed9.000MB/1s 2018/08/1700:24:52[*]Speed11.125MB/1s 2018/08/1700:24:53[*]Speed11.125MB/1s 2018/08/1700:24:54[*]Speed10.562MB/1s 2018/08/1700:24:55[*]Speed11.187MB/1s 2018/08/1700:24:56[*]Speed11.109MB/1s 2018/08/1700:24:57[*]Speed11.109MB/1s 2018/08/1700:24:58[*]Speed11.141MB/1s 2018/08/1700:24:59[*]Speed11.172MB/1s 2018/08/1700:25:00[*]Speed11.141MB/1s 2018/08/1700:25:01[*]Speed8.453MB/1s 2018/08/1700:25:02[*]Speed6.385MB/1s 2018/08/1700:25:03[*]Speed0Bytes/1s
这是最终的运行结果,然后我再把全部的源码放在下面就去睡了。
packagemain
import(
"net/http"
"log"
"time"
"net/url"
"path"
"os"
"io"
"bufio"
"math"
"strconv"
)
vardurl="https://dl.google.com/go/go1.10.3.darwin-amd64.pkg";
funcmain(){
uri,err:=url.ParseRequestURI(durl)
iferr!=nil{
panic("网址错误")
}
filename:=path.Base(uri.Path)
log.Println("[*]Filename"+filename)
client:=http.DefaultClient;
client.Timeout=time.Second*60//设置超时时间
resp,err:=client.Get(durl)
iferr!=nil{
panic(err)
}
ifresp.ContentLength<=0{
log.Println("[*]Destinationserverdoesnotsupportbreakpointdownload.")
}
raw:=resp.Body
deferraw.Close()
reader:=bufio.NewReaderSize(raw,1024*32);
file,err:=os.Create(filename)
iferr!=nil{
panic(err)
}
writer:=bufio.NewWriter(file)
buff:=make([]byte,32*1024)
written:=0
gofunc(){
for{
nr,er:=reader.Read(buff)
ifnr>0{
nw,ew:=writer.Write(buff[0:nr])
ifnw>0{
written+=nw
}
ifew!=nil{
err=ew
break
}
ifnr!=nw{
err=io.ErrShortWrite
break
}
}
ifer!=nil{
ifer!=io.EOF{
err=er
}
break
}
}
iferr!=nil{
panic(err)
}
}()
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())
ifwritten-lastWtn==0{
ticker.Stop()
stop=true
break
}
lastWtn=written
}
ifstop{
break
}
}
}
funcbytesToSize(lengthint)string{
vark=1024//or1024
varsizes=[]string{"Bytes","KB","MB","GB","TB"}
iflength==0{
return"0Bytes"
}
i:=math.Floor(math.Log(float64(length))/math.Log(float64(k)))
r:=float64(length)/math.Pow(float64(k),i)
returnstrconv.FormatFloat(r,'f',3,64)+""+sizes[int(i)]
}
以上这篇Golang使用httpClient下载文件的实现方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
