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


猜你喜欢
- 对于Python内置函数sorted(),先拿来跟list(列表)中的成员函数list.sort()进行下对比。在本质上,list的排序和内
- 1.如何用函数先定义后调用,定义阶段只检测语法,不执行代码调用阶段,开始执行代码函数都有返回值定义时无参,调用时也是无参定义时有参,调用时也
- 本文所述为C#实现的Oracle数据库操作类,可执行超多常用的Oracle数据库操作,包含了基础数据库连接、关闭连接、输出记录集、执行Sql
- 一:购物车管理功能1.添加商品(不重复添加)、2.删除商品(购物车中有的才能删除)、3.查看购物车4.退出系统产品列表products =
- 一个不错的文字放大特性源码。效果图:运行代码框<script for=document event=onmousemove>//
- 形参可以设置参数默认值,设置遵循从右至左原则例如:fun(x=0,y=1),fun(x,y=1),但不可以是fun(x=1,y)形参设置可以
- 一开始学习 Python 的时候习惯性的使用 C 中的 a++ 这种写法,发现会报 SyntaxError: invalid syntax
- 根据教程实现了读取csv文件前面的几行数据,一下就想到了是不是可以实现前面几列的数据。经过多番尝试总算试出来了一种方法。之所以想实现读取前面
- 这是一个很和谐很实用的网站管理程序,和我以前介绍的服务器管理程序不同的是,这个程序只有一个功能,就是实现远程Web方式删除文件(实际上是重命
- 请求钩子通过装饰器为一个模块添加请求钩子, 对当前模块的请求进行额外的处理. 比如权限验证.说白了,就是在执行视图函数前后你可以进行一些处理
- gRPC HTTP协议转换正当有这个需求的时候,就看到了这个实现姿势。源自coreos的一篇博客,转载到了grpc官方博客gRPC with
- 前言一个简单的php➕mysql项目学生信息管理系统,用于广大学子完成期末作业的参考,该系统实现增、删、改、查等基本功能。1、登录界面<
- 方法一://PHP stdClass Object转array function object_array($array) {&
- 本文介绍什么是链表,常见的链表有哪些,然后介绍链表这种数据结构会在哪些地方可以用到,以及 Redis 队列是底层的实现,通过一个小实例来演示
- 听到一些人说现在做产品设计很没有成就感。没有什么创造力,除了抄袭模仿(称之为竞争分析)、千篇一律(又称规范标准)还有复杂的流程、粗制滥造的表
- 什么是探索性数据分析(EDA)?EDA 是数据分析下的一种现象,用于更好地理解数据方面,例如: – 数据的主要
- 在SQL查询分析器执行以下代码就可以了。declare @t varchar(255),@c varchar(255)declare tab
- 想到TDE(Transparent Data Encryption)。 TDE MSDN 说明: “透明数据加密”(TDE) 可对数据和日志
- 1.文件的写入和读取#!/usr/bin/python # -*- coding: utf-8 -*- # Filename: using_
- 关于这个问题的解答,网络上有成千上万个版本,当然,出现问题的诱因不同,解决的途径也不同,所以我不排除其他解决此类问题的办法,但是这些办法都没