分享
  1. 首页
  2. 文章

Golang实现多线程下载

DJ沸羊羊 · · 5748 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前段时间写了个小爬虫,从国外某网站上下载视频,初期使用的是单线下载,后面发现访问服务端的资源数过多,会被反爬机制限制,还有一个问题就是单线下载境外网站内容,效率比较低,下载速度很慢,后面修修补补改了改,改为多线访问同个资源,顺利解决反爬机制,也提升了下载效率.

多线程下载必须服务端支持

 1.判断服务端是否支持多线下载:
 使用 HEAD 方法请求资源,然后查看服务端返回数据
image.png
 2.查看返回数据头部是否存在 `Accept-Ranges →bytes`
 如果有,那么就支持多线程下载,没有的话基本上可以洗洗睡了.

Golang 实现环节

`

最近写了个小爬虫,从国外某网站上下载视频,初期使用的是单线下载,后面发现访问服务端的资源数过多,会被反爬机制限制,还有一个问题就是单线下载境外网站内容性能比较差,下载速度很慢,后面修修补补改了改,改为多线访问同个资源,顺利解决反爬机制,也提升了下载效率.

多线程下载必须服务端支持

 1.判断服务端是否支持多线下载:
 使用 HEAD 方法请求资源,然后查看服务端返回数据
image.png
 2.查看返回数据头部是否存在 `Accept-Ranges →bytes`
 如果有,那么就支持多线程下载,没有的话基本上可以洗洗睡了.

Golang 实现环节

判断是否支持多线下载

image.png

多线下载任务分配

image.png

执行下载

image.png

完整代码

package download
import (
 "github.com/labstack/gommon/log"
 "io/ioutil"
 "net/http"
 "os"
 "strconv"
 "strings"
 "sync"
 "sync/atomic"
 "time"
)
var client = http.Client{Timeout: time.Second * 180}
var threadGroup = sync.WaitGroup{}
var packageSize int64
func init() {
 //每个线程下载文件的大小
 packageSize = 1048576 * 4
}
func Download(url, cachePath string, scheduleCallback func(schedule float64)) string {
 var localFileSize int64
 var file *os.File
 if info, e := os.Stat(cachePath); e != nil {
 if os.IsNotExist(e) {
 if createFile, err := os.Create(cachePath); err == nil {
 file = createFile
 } else {
 panic(err)
 }
 } else {
 panic(e)
 }
 } else {
 localFileSize = info.Size()
 }
 //HEAD 方法请求服务端是否支持多线程下载,并获取文件大小
 if request, e := http.NewRequest("HEAD", url, nil); e == nil {
 if response, i := client.Do(request); i == nil {
 defer response.Body.Close()
 //得到文件大小
 ContentLength := response.ContentLength
 if localFileSize == ContentLength {
 log.Warn("file exist~")
 return cachePath
 } else {
 //判断是否支持多线下载
 if strings.Compare(response.Header.Get("Accept-Ranges"), "bytes") == 0 {
 //支持 走下载流程
 if dispSliceDownload(file, ContentLength, url, scheduleCallback) == 0 {
 return cachePath
 } else {
 return ""
 }
 } else {
 panic("nonsupport ~")
 }
 }
 } else {
 panic(i)
 }
 } else {
 panic(e)
 }
 return ""
}
func dispSliceDownload(file *os.File, ContentLength int64, url string, scheduleCallback func(schedule float64)) int {
 defer file.Close()
 //文件总大小除以 每个线程下载的大小
 i := ContentLength / packageSize
 //保证文件下载完整
 if ContentLength%packageSize > 0 {
 i += 1
 }
 //下载总进度
 var schedule int64
 //分配下载线程
 for count := 0; count < int(i); count++ {
 //计算每个线程下载的区间,起始位置
 var start int64
 var end int64
 start = int64(int64(count) * packageSize)
 end = start + packageSize
 if int64(end) > ContentLength {
 end = end - (end - ContentLength)
 }
 //构建请求
 if req, e := http.NewRequest("GET", url, nil); e == nil {
 req.Header.Set(
 "Range",
 "bytes="+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10))
 //
 threadGroup.Add(1)
 go sliceDownload(req, file, &schedule, &ContentLength, scheduleCallback, start)
 } else {
 panic(e)
 }
 }
 //等待所有线程完成下载
 threadGroup.Wait()
 return 0
}
func sliceDownload(request *http.Request, file *os.File, schedule *int64, ContentLength *int64, scheduleCallback func(schedule float64),
 start int64) {
 defer threadGroup.Done()
 if response, e := client.Do(request); e == nil && response.StatusCode == 206 {
 defer response.Body.Close()
 if bytes, i := ioutil.ReadAll(response.Body); i == nil {
 i2 := len(bytes)
 //从我们计算好的起点写入文件
 file.WriteAt(bytes, start)
 atomic.AddInt64(schedule, int64(i2))
 val := atomic.LoadInt64(schedule)
 num := float64(val*1.0) / float64(*ContentLength) * 100
 scheduleCallback(float64(num))
 } else {
 panic(e)
 }
 } else {
 panic(e)
 }
}

因为硬盘空间有限,爬取到180GB的时候,就结束了爬取.

image.png
个人联系方式:
作者QQ:853151446
作者邮箱:853151446@qq.com
有问题或者可以一起探讨的,请联系我,或者留言.

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:DJ沸羊羊

查看原文:Golang实现多线程下载

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
5748 次点击
1 回复 | 直到 2020年09月27日 14:16:05
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏