Go源码实现使用多线程并发下载大文件的功能

摘要:Go语言编码实现了使用多线程并发下载文件的功能。

1. 代码流程介绍

1. 获取系统的CPU核心数量,并将其作为线程数的参考值,并打印出来。
2. 定义要下载的文件的URL、线程数和输出文件名。
3. 使用`getFileSize()`函数获取文件大小,并打印出来。
4. 根据文件大小和线程数计算文件块大小,如果是最后一次线程的结尾设置为文件结尾,确保文件下载的完整性。
5. 创建一个等待组(`sync.WaitGroup`),用于确保所有下载完成后再合并文件。
6. 创建一个通道(`chunkPaths`),用于接收下载完成的文件块路径。
7. 启动多个goroutine并发下载文件块,每个goroutine负责下载指定范围的文件块。
8. 每个goroutine使用`downloadChunk()`函数下载文件块,并将下载完成的文件块路径发送到通道。
9. 等待所有下载完成,然后关闭通道,表示所有文件块都已下载完成。
10. 创建一个输出文件。
11. 使用`mergeChunk()`函数将下载的文件块合并到输出文件中,并在合并过程中打印合并成功或失败的信息。
12. 在合并完成后,删除临时的文件块。
13. 打印文件下载完成的消息。

源码通过并发下载文件块,利用多线程来加快文件下载的速度。每个线程负责下载文件的一个部分,下载完成后将文件块合并到最终的输出文件中。通过合理设置线程数,可以充分利用可用的CPU资源,提高下载效率。

请注意,代码中使用了`http`和`os`包来进行文件下载和操作,需要保证网络连接正常,并且有足够的权限来创建和删除文件。

2. Go完整源码

package mainimport ("fmt""io""net/http""os""runtime""sync"
)const (fileURL = "http://example.com/large-file.zip" // 要下载的文件URL// threads    = 5                                                                                                // 并发下载的线程数outputFile = "output.zip" // 下载完成后的输出文件名
)func main() {// CPU数量作为线程数量numCPU := runtime.NumCPU()fmt.Println("CPU核心数量:", numCPU)threads := numCPUfmt.Println("多线程数量:", threads)fmt.Println("开始下载文件...")// 获取文件大小fileSize, err := getFileSize(fileURL)if err != nil {fmt.Println("无法获取文件大小:", err)return}fmt.Println("文件大小:", fileSize, "bytes")// 计算文件块大小chunkSize := fileSize / int64(threads)// 创建等待组,确保所有下载完成后再合并文件var wg sync.WaitGroupwg.Add(threads)// 创建一个通道用于接收下载完成的文件块路径chunkPaths := make(chan string, threads)// 启动多个 goroutine 并发下载文件块for i := 0; i < threads; i++ {go func(index int) {defer wg.Done()start := int64(index) * chunkSizeend := start + chunkSize - 1// 如果是最后一次线程的结尾设置为文件结尾,确保文件下载的完整性if index == threads-1 {end = fileSize - 1}fmt.Printf("线程 %d 开始下载:%d-%d\n", index, start, end)chunkPath, err := downloadChunk(fileURL, start, end)if err != nil {fmt.Printf("线程 %d 下载失败:%v\n", index, err)} else {fmt.Printf("线程 %d 下载完成:%d-%d\n", index, start, end)chunkPaths <- chunkPath // 将下载完成的文件块路径发送到通道}}(i)}// 等待所有下载完成wg.Wait()close(chunkPaths) // 关闭通道,表示所有文件块都已下载完成// 创建一个输出文件output, err := os.Create(outputFile)if err != nil {fmt.Println("无法创建输出文件:", err)return}defer output.Close()// 合并下载的文件块到输出文件for chunkPath := range chunkPaths {fmt.Println("合并文件块:", chunkPath)err := mergeChunk(chunkPath, output)if err != nil {fmt.Printf("合并文件块失败:%v\n", err)} else {fmt.Printf("合并文件块成功:%s\n", chunkPath)}// 删除临时文件块err = os.Remove(chunkPath)if err != nil {fmt.Printf("删除文件块失败:%v\n", err)}}fmt.Println("文件下载完成。")
}// 获取文件大小
func getFileSize(url string) (int64, error) {resp, err := http.Head(url)if err != nil {return 0, err}defer resp.Body.Close()if resp.StatusCode != http.StatusOK {return 0, fmt.Errorf("服务器返回错误: %v", resp.Status)}return resp.ContentLength, nil
}// 下载文件块
func downloadChunk(url string, start, end int64) (string, error) {req, err := http.NewRequest("GET", url, nil)if err != nil {return "", err}req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end))resp, err := http.DefaultClient.Do(req)if err != nil {return "", err}defer resp.Body.Close()if resp.StatusCode != http.StatusPartialContent {return "", fmt.Errorf("服务器不支持分块下载:%v", resp.Status)}// 创建一个临时文件用于保存下载的文件块chunkPath := fmt.Sprintf("chunk_%d_%d.tmp", start, end)chunkFile, err := os.Create(chunkPath)if err != nil {return "", err}defer chunkFile.Close()_, err = io.Copy(chunkFile, resp.Body)if err != nil {return "", err}return chunkPath, nil
}// 合并文件块
func mergeChunk(chunkPath string, output *os.File) error {chunkFile, err := os.Open(chunkPath)if err != nil {return err}defer chunkFile.Close()_, err = io.Copy(output, chunkFile)if err != nil {return err}return nil
}

3. 执行结果

> go run .\largefile_download_goroutine.go
CPU核心数量: 8
多线程数量: 8
开始下载文件...
文件大小: 28057414 bytes
线程 7 开始下载:24550232-28057413
线程 1 开始下载:3507176-7014351
线程 0 开始下载:0-3507175
线程 4 开始下载:14028704-17535879
线程 3 开始下载:10521528-14028703
线程 5 开始下载:17535880-21043055
线程 6 开始下载:21043056-24550231
线程 2 开始下载:7014352-10521527
线程 0 下载完成:0-3507175
线程 7 下载完成:24550232-28057413
线程 3 下载完成:10521528-14028703
线程 1 下载完成:3507176-7014351
线程 2 下载完成:7014352-10521527
线程 4 下载完成:14028704-17535879
线程 6 下载完成:21043056-24550231
线程 5 下载完成:17535880-21043055
合并文件块: chunk_0_3507175.tmp
合并文件块成功:chunk_0_3507175.tmp
合并文件块: chunk_24550232_28057413.tmp
合并文件块成功:chunk_24550232_28057413.tmp
合并文件块: chunk_10521528_14028703.tmp
合并文件块成功:chunk_10521528_14028703.tmp
合并文件块: chunk_3507176_7014351.tmp
合并文件块成功:chunk_3507176_7014351.tmp
合并文件块: chunk_7014352_10521527.tmp
合并文件块成功:chunk_7014352_10521527.tmp
合并文件块: chunk_14028704_17535879.tmp
合并文件块成功:chunk_14028704_17535879.tmp
合并文件块: chunk_21043056_24550231.tmp
合并文件块成功:chunk_21043056_24550231.tmp
合并文件块: chunk_17535880_21043055.tmp
合并文件块成功:chunk_17535880_21043055.tmp
文件下载完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/118908.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【AI视野·今日Robot 机器人论文速览 第五十七期】Wed, 18 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Wed, 18 Oct 2023 Totally 17 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Underwater and Surface Aquatic Locomotion of Soft Biomimetic Robot Based on Bending Rolled Dielectric Elastomer Actua…

使用ION-SFU和媒体设备在Golang中构建一个WebRTC视频和音频广播器

在本教程中&#xff0c;您将构建一个视频广播应用程序&#xff0c;该应用程序在 Golang 中读取摄像头并将其发送到 ION-SFU&#xff08;选择性转发单元&#xff09;&#xff0c;从而使 WebRTC 会话更有效地扩展。 WebRTC 是 Web Real-Time Communication 的缩写&#xff0c;是…

No module named ‘cv2’ 解决方法

目录 解决方案1解决方案2 解决方案1 一般情况下的解决方案 在自己的虚拟环境里面安装就行 pip install opencv-python解决方案2 但是我遇到的情况没有这么简单,我使用了pip list | grep open 搜索含有open字样的opencv的包,结果显示已经安装了 我直接进入我的自定义的虚拟…

亚马逊注册账号时老是显示内部错误

最近你们是否遇到注册亚马逊账号时一直遇到"内部错误"的情况&#xff1f;&#xff0c;这可能是由多种原因引起的。以下是一些可能有助于解决这个问题的步骤&#xff1a; 1、清除缓存和Cookie&#xff1a;有时浏览器缓存和Cookie中的问题可能导致网站错误。可以试试清…

selenium模拟登录无反应

在使用自动化工具selenium时对某些网站登录界面send_keys输入账号密码&#xff0c;运行时却没有自己想要的结果出现&#xff0c;这是因为你碰到前端二般的开发人员&#xff0c;他们用的是HTML嵌套&#xff0c;这对后端人员造成了一些麻烦&#xff0c;废话不多说&#xff0c;直接…

5G vs 4G

5G与4G的关键性能指标对比 指标名称流量密度连接密度空口时延移动性能效指标用户体验速率频谱效率峰值速率4G 参考值0.1 M b i t / s / m 2 Mbit/s/m^2 Mbit/s/m2 1 ∗ 1 0 5 / k m 2 1*10^5/km^2 1∗105/km210ms350km/h1倍10Mbit/s1倍1Gbit/s5G 参考值10 M b i t / s / m 2 M…

Go并发:使用sync.Pool来性能优化

简介 在Go提供如何实现对象的缓存池功能&#xff1f;常用一种实现方式是&#xff1a;sync.Pool, 其旨在缓存已分配但未使用的项目以供以后重用&#xff0c;从而减轻垃圾收集器&#xff08;GC&#xff09;的压力。 快速使用 sync.Pool的结构也比较简单&#xff0c;常用的方法…

如何在Ubuntu下安装RabbitMQ服务并异地远程访问?

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

WeakHashMap 源码解析

目录 一. 前言 二. 源码解析 2.1. 类结构 2.2. 成员变量 2.3. 构造方法 2.4. Entry 2.5. 添加元素 2.6. 扩容 2.7. 删除元素 2.8. 获取元素 一. 前言 WeakHashMap&#xff0c;从名字可以看出它是某种 Map。它的特殊之处在于 WeakHashMap 里的entry可能会被GC自动删除…

新手投资如何分配股票仓位?诺奖得主的秘诀是什么?| 附代码【邢不行】

2023年6月22日&#xff0c;诺贝尔经济学奖得主哈里.马克维茨于美国去世&#xff0c;享年95岁。 作为现代金融先驱者&#xff0c;马科维茨不仅是将数学引入金融的第一人&#xff0c;更用数学解释了分散投资的重要性。 更令人惊叹的是&#xff0c;过去十几年中如果按他的理论在中…

docker部署prometheus+grafana服务器监控(一)

docker-compose 部署prometheusgrafana Prometheus Prometheus 是有 SoundCloud 开发的开源监控系统和时序数据库&#xff0c;基于 Go 语言开发。通过基于 HTTP 的 pull 方式采集时序数据&#xff0c;通过服务发现或静态配置去获取要采集的目标服务器&#xff0c;支持多节点工…

18.2 使用NPCAP库抓取数据包

NPCAP 库是一种用于在Windows平台上进行网络数据包捕获和分析的库。它是WinPcap库的一个分支&#xff0c;由Nmap开发团队开发&#xff0c;并在Nmap软件中使用。与WinPcap一样&#xff0c;NPCAP库提供了一些API&#xff0c;使开发人员可以轻松地在其应用程序中捕获和处理网络数据…

clickhouse、Doris、Kylin对比

clickhouse ClickHouse是俄罗斯的Yandex于2016年开源的列式存储数据库&#xff08;DBMS&#xff09;&#xff0c;使用C语言编写&#xff0c;是基于 MPP 架构的分布式 ROLAP &#xff08;Relational OLAP&#xff09;分析引擎主要用于在线分析处理查询&#xff08;OLAP&#xff…

【Redis系列】在Centos7上安装Redis5.0保姆级教程!

哈喽&#xff0c; 大家好&#xff0c;我是小浪。那么最近也是在忙秋招&#xff0c;很长一段时间没有更新文章啦&#xff0c;最近呢也是秋招闲下来&#xff0c;当然秋招结果也不是很理想&#xff0c;嗯……这里就不多说啦&#xff0c;回归正题&#xff0c;从今天开始我们就开始正…

SaveFileDialog.OverwritePrompt

SaveFileDialog.OverwritePrompt 获取或设置一个值&#xff0c;该值指示如果用户指定的文件名已存在&#xff0c;Save As 对话框是否显示警告。 public bool OverwritePrompt { get; set; } OverwritePrompt 控制在将要在改写现在文件时是否提示用户 https://vimsky.com/…

Elasticsearch:使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation (三)

这是继之前文章&#xff1a; Elasticsearch&#xff1a;使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation &#xff08;一&#xff09; Elasticsearch&#xff1a;使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation &#xff08;二&…

Python必学函数:常用内置函数详解和举例分析

map函数 是根据第一个参数定义的函数&#xff0c;依次作用在序列上&#xff0c;返回一个迭代器 s 1,2,3,4,5 # 将字符串转换成整数列表 list(map(int, s.split(,))) # [1,2,3,4,5]# 求两个连表中元素的和&#xff0c;放入新列表中 data1 [1,2,3] data2 [4,5,6] list(map(l…

Fedora Linux 38下安装音频与视频的解码器和播放器

Fedora Linux 38 操作系统安装好后&#xff0c;默认是没有音频与视频的解码器的&#xff0c;音频与视频的播放体验非常差劲。但是第三方的软件源中有解码器和播放器的软件&#xff0c;需要我们自己手动安装。、 连接互联网&#xff0c;打开Shell命令行&#xff1a; 1. sudo d…

配置Sentinel 控制台

1.遇到的问题 服务网关 | RuoYi 最近调试若依的微服务版本需要用到Sentinel这个组件&#xff0c;若依内部继承了这个组件连上即用。 Sentinel是阿里巴巴开源的限流器熔断器&#xff0c;并且带有可视化操作界面。 在日常开发中&#xff0c;限流功能时常被使用&#xff0c;用…

uni-app配置微信开发者工具

一、配置微信开发者工具路径 工具->设置->运行配置->小程序运行配置->微信开发者工具路径 二、微信开发者工具开启服务端口