Golang 使用http Client下载文件的实现方法

作者:夕阳醉了半边天 时间:2023-07-21 07:32:23 

之前使用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
}
}

这段代码堵塞住程序,然后定时器按照间隔时间来计算速度。这里可能会出现bug的地方就是在间隔时间内没有读取到数据的时候回直接跳出循环。现在这个时间点非常困,写个blog已经困的要死了以后有空把这个bug解决了吧


func bytesToSize(length int) string {
var k = 1024 // or 1024
var sizes = []string{"Bytes", "KB", "MB", "GB", "TB"}
if length == 0 {
 return "0 Bytes"
}
i := math.Floor(math.Log(float64(length)) / math.Log(float64(k)))
r := float64(length) / math.Pow(float64(k), i)
return strconv.FormatFloat(r, 'f', 3, 64) + " " + sizes[int(i)]
}

这个函数是我从我个人的php项目中转换过来的


2018/08/17 00:24:50 [*] Filename go1.10.3.darwin-amd64.pkg
2018/08/17 00:24:51 [*] Speed 9.000 MB / 1s
2018/08/17 00:24:52 [*] Speed 11.125 MB / 1s
2018/08/17 00:24:53 [*] Speed 11.125 MB / 1s
2018/08/17 00:24:54 [*] Speed 10.562 MB / 1s
2018/08/17 00:24:55 [*] Speed 11.187 MB / 1s
2018/08/17 00:24:56 [*] Speed 11.109 MB / 1s
2018/08/17 00:24:57 [*] Speed 11.109 MB / 1s
2018/08/17 00:24:58 [*] Speed 11.141 MB / 1s
2018/08/17 00:24:59 [*] Speed 11.172 MB / 1s
2018/08/17 00:25:00 [*] Speed 11.141 MB / 1s
2018/08/17 00:25:01 [*] Speed 8.453 MB / 1s
2018/08/17 00:25:02 [*] Speed 6.385 MB / 1s
2018/08/17 00:25:03 [*] Speed 0 Bytes / 1s

这是最终的运行结果,然后我再把全部的源码放在下面就去睡了。


package main

import (
"net/http"
"log"
"time"
"net/url"
"path"
"os"
"io"
"bufio"
"math"
"strconv"
)

var durl = "https://dl.google.com/go/go1.10.3.darwin-amd64.pkg";

func main() {
uri, err := url.ParseRequestURI(durl)
if err != nil {
 panic("网址错误")
}

filename := path.Base(uri.Path)
log.Println("[*] Filename " + filename)

client := http.DefaultClient;
client.Timeout = time.Second * 60 //设置超时时间
resp, err := client.Get(durl)
if err != nil {
 panic(err)
}
if resp.ContentLength <= 0 {
 log.Println("[*] Destination server does not support breakpoint download.")
}
raw := resp.Body
defer raw.Close()
reader := bufio.NewReaderSize(raw, 1024*32);

file, err := os.Create(filename)
if err != nil {
 panic(err)
}
writer := bufio.NewWriter(file)

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)
 }
}()

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
 }
}
}

func bytesToSize(length int) string {
var k = 1024 // or 1024
var sizes = []string{"Bytes", "KB", "MB", "GB", "TB"}
if length == 0 {
 return "0 Bytes"
}
i := math.Floor(math.Log(float64(length)) / math.Log(float64(k)))
r := float64(length) / math.Pow(float64(k), i)
return strconv.FormatFloat(r, 'f', 3, 64) + " " + sizes[int(i)]
}

来源:https://blog.csdn.net/a99361481/article/details/81751231

标签:Golang,http,Client,下载文件
0
投稿

猜你喜欢

  • OpenCV+python手势识别框架和实例讲解

    2021-09-06 19:47:43
  • jquery ui bootstrap 实现自定义风格

    2024-05-09 09:05:24
  • ASP连接Access数据库和SQL server数据库的方法

    2007-08-22 13:16:00
  • python中os包的用法

    2021-07-11 07:24:02
  • django中的数据库迁移的实现

    2024-01-18 07:35:00
  • 对dataframe进行列相加,行相加的实例

    2023-07-24 16:57:59
  • 网站的视觉路径和版式设计

    2008-04-15 14:35:00
  • Mysql中的自连接问题

    2024-01-17 17:55:43
  • python数据处理 根据颜色对图片进行分类的方法

    2022-02-27 08:41:37
  • python 读取串口数据的示例

    2021-08-30 11:10:26
  • python palywright库基本使用

    2021-03-30 17:34:42
  • Python程序打包工具py2exe和PyInstaller详解

    2021-04-17 05:04:12
  • python实现彩色图转换成灰度图

    2022-03-11 02:41:30
  • 简单了解Go语言中函数作为值以及函数闭包的使用

    2024-04-29 13:05:50
  • 类似google的ASP分页代码[测试通过]

    2009-03-13 12:43:00
  • js父页面中使用子页面的方法

    2024-04-19 09:50:49
  • Oracle外键不加索引引起死锁示例

    2024-01-20 20:10:08
  • 关于应用UI组件的移动端适配方式

    2024-04-27 16:12:01
  • Python 命令行非阻塞输入的小例子

    2023-12-09 19:48:29
  • selenium+python自动化测试之鼠标和键盘事件

    2022-10-28 03:32:25
  • asp之家 网络编程 m.aspxhome.com