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
文件下载完成。
相关文章:
Go源码实现使用多线程并发下载大文件的功能
摘要:Go语言编码实现了使用多线程并发下载文件的功能。 1. 代码流程介绍 1. 获取系统的CPU核心数量,并将其作为线程数的参考值,并打印出来。 2. 定义要下载的文件的URL、线程数和输出文件名。 3. 使用getFileSize()函数获取文件大小…...
Python基础入门例程1-NP1 Hello World!
描述 将字符串 Hello World! 存储到变量str中,再使用print语句将其打印出来。 输入描述: 无 输出描述: 一行输出字符串Hello World! 解答: str "Hello World!" print(str) 解释说明: 赋值变量&…...
前端面试题10.23
解决的最复杂的前端问题,介绍一下 最复杂的前端问题之一是浏览器兼容性,不同浏览器对网页的渲染方式存在差异,需要针对不同浏览器做兼容性处理。此外,前端性能优化也是一个复杂的问题,需要综合考虑网页加载速度、渲染…...
DYC算法开发与测试(基于ModelBase实现)
ModelBase是经纬恒润开发的车辆仿真软件,包含两个大版本:动力学版本、智能驾驶版本。动力学版包含高精度动力学模型,能很好地复现车辆在实际道路中运行的各种状态变化,可用于乘用车、商用车动力底盘系统算法开发、控制器仿真测试&…...
第四章 路由基础
目录 4.1 路由器概述 4.1.1 路由器定义 4.1.2 路由器工作原理 4.1.3 路由表的生成方式 (1)直连路由 (2)静态路由 (3)动态路由 4.1.4 路由器的接口 (1)配置接口 ࿰…...
Java逻辑运算符(、||和!),Java关系运算符
逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式,以判断程序中的表达式是否成立,判断的结果是 true 或 false。 逻辑运算符是对布尔型变量进行运算,其结果也是布尔型,具体如表 1 所示。 表 1 逻辑运算符的用…...
三个设备文件
...
Java赋值运算符(=)
赋值运算符是指为变量或常量指定数值的符号。赋值运算符的符号为“”,它是双目运算符,左边的操作数必须是变量,不能是常量或表达式。 其语法格式如下所示: 变量名称表达式内容 在 Java 语言中,“变量名称”和“表达式…...
提升药店效率:山海鲸医药零售大屏的成功案例
在医药行业中,特别是医药零售领域,高效的药品管理和客户服务至关重要。随着科技的飞速发展,数字化解决方案已经成为提高医药零售管控效率的有效工具之一。其中,医药零售管控大屏作为一种强大的工具,正在以独特的方式改…...
使用Fragement(碎片)
一、Fragment简介 屏幕大小的差距可能会使同样的界面在不同设备上显示出不同的效果,为了能同时兼顾到手机和平板电脑的开发,从Android3.0版本开始提供了Fragment。 Fragment(碎片)是一种嵌入在Activity中的UI片段,它…...
种花问题(Python题目)
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。 给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 …...
STM32入门F4
学习资料:杨桃电子,官网:洋桃电子 | 杜洋工作室 www.doyoung.net 嵌入式开发核心宗旨:以最适合的性能、功能、成本来完成最有性价比的产品开发。 1.为什么要学F407 STM32F103系列与STM32F407系列对照表: 2.F4系列命…...
【30】c++设计模式——>状态模式
状态模式概述 状态模式是一种行为型设计模式,它可以让一个对象在其内部状态发生变化时更改其行为。通过将每个状态封装成一个独立的类,我们可以使状态之间互相独立,并且使得状态的切换变得更加灵活、可扩展。(多个状态之间可以相…...
中文编程开发语言编程实际案例:程序控制灯电路以及桌球台球室用这个程序计时计费
中文编程开发语言编程实际案例:程序控制灯电路以及桌球台球室用这个程序计时计费 上图为:程序控制的硬件设备电路图 上图为:程序控制灯的开关软件截图,适用范围比如:台球厅桌球室的计时计费管理,计时的时候…...
【python】高斯日记
题目: """ 题目描述: 高斯出生于1777年4月30日,记作1777-4-30。在此基础上,我们希望你写一个程序,给定一个数字n,表示从高斯出生的那天算起的第n天,输出这一天的具体日期。例如&…...
[论文笔记]MobileBERT
引言 今天带来一篇关于量化的论文MobileBERT,题目翻译过来是:一种适用于资源有限设备的紧凑型任务无关BERT模型。模型的简称是MobileBERT,意思是作者的这个BERT模型可以部署到手机端。 本篇工作,作者提出了MobileBERT用于压缩和加速BERT模型。与原始BERT一样,MobileBERT…...
【Spring Cloud】如何确定微服务项目的Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本
文章目录 1. 版本选择2. 用脚手架快速生成微服务的pom.xml3. 创建一个父工程4. 代码地址 本文描述如何确定微服务项目的Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本。 1. 版本选择 我们知道Spring Boot、Spring Cloud、Spring Cloud Alibaba的版本选择一致性非常重…...
Power BI 傻瓜入门 1. 数据分析术语:Power BI风格
本章内容包括: 了解Power BI可以处理的不同类型的数据了解您的商业智能工具选项熟悉Power BI术语 数据无处不在。从你醒来的那一刻到你睡觉的时候,某个系统会代表你收集数据。即使在你睡觉的时候,也会产生与你生活的某些方面相关的数据。如…...
【C++和数据结构】位图和布隆过滤器
目录 一、位图 1、位图的概念 2、位图的实现 ①、基本结构 ②、set ③、reset: ④、test ⑤、问题: ⑥、位图优缺点及应用: ⑦、完整代码及测试 二、布隆过滤器 1、布隆过滤器的提出 2、布隆过滤器的实现 ①、基本结构 ②…...
Mybatis分页
本文主要讲解Mybatis分页相关的技术分享,如果觉得不错的话,就点个赞吧。。。。 Mybatis分页主要有2种类型: 一、物理分页: 1、定义: 物理分页是在数据库层面进行的分页,即通过SQL语句直接从数据库中查询…...
聊一聊 C# 中的闭包陷阱:foreach 循环的坑你还记得吗?嘲
. GIF文件结构 相比于 WAV 文件的简单粗暴,GIF 的结构要精密得多,因为它天生是为了网络传输而设计的(包含了压缩机制)。 当我们用二进制视角观察 GIF 时,它是由一个个 数据块(Block) 组成的&…...
国产长芯微LPS7172完全P2P替代ADM7172,是一款CMOS低压差线性稳压器
描述LPS7172是一款CMOS低压差线性稳压器,工作电压范围为2.3V至6.5V,可提供高达2A的输出电流。这款高输出电流LDO非常适合用于调节基于6V至1.2V电源轨的高性能模拟与混合信号电路。采用先进的专利架构,该器件具有高电源抑制比和低噪声特性&…...
MCP23S17 SPI端口扩展器原理与Arduino驱动实战
1. MCP23S17 嵌入式SPI端口扩展器深度技术解析MCP23S17 是 Microchip 公司推出的 16 通道、SPI 接口的可编程 I/O 端口扩展芯片,广泛应用于资源受限的嵌入式系统中,用于扩展主控 MCU 的 GPIO 数量。其核心价值在于以极低的硬件开销(仅需 4 根…...
明明知道该做什么,却总提不起劲?蕙兰瑜伽告诉你:不是你懒,是你忘了自己是谁
你有没有过这样的早晨:醒来后很清楚有两件明确的事情要做,比如打扫卫生、学习一门课程,但就是坐在那里不想动?你并不迷茫,也知道该干什么,可那种“做事的感觉”就是上不来。如果你最近经历过离婚、重大转折…...
MedGemma Medical Vision Lab用于模型对比研究:与LLaVA-Med、RadFM等多模态模型性能横评
MedGemma Medical Vision Lab用于模型对比研究:与LLaVA-Med、RadFM等多模态模型性能横评 1. 引言:医学多模态模型的发展现状 医学影像分析正经历着从传统算法向多模态大模型的转型。随着GPT-4V、Gemini等通用多模态模型的突破,医学领域也涌…...
【PCIe 验证每日学习・Day29】PCIe 链路训练与 LTSSM 状态机全解析
大家好,今天我们进入 PCIe 最核心、最基础,同时也是所有链路问题根源的模块:LTSSM 链路训练状态机。内容承接此前电源管理、虚拟化、中断等知识,从 “上电链路建立” 到 “异常恢复” 完整拆解,所有描述严格遵循 PCIe …...
SmartX CloudTower 2.0安全指南:从权限配置到等保合规的完整设置流程
SmartX CloudTower 2.0安全指南:从权限配置到等保合规的完整设置流程 在数字化转型加速的今天,企业IT基础设施的安全管理已成为重中之重。特别是对于金融、医疗等高度监管行业,如何构建既满足业务需求又符合严格合规要求的安全体系࿰…...
保姆级教程:基于ROS Melodic和MoveIt!,手把手搭建双RM65机械臂协同控制系统
基于ROS Melodic和MoveIt!的双RM65机械臂协同控制系统实战指南 在工业自动化与智能制造领域,双机械臂协同作业正成为提升生产效率的关键技术。想象一下,两台机械臂如同默契的舞者,在装配线上精准配合,完成单台设备难以企及的复杂任…...
cmake之旅(12)
cmake之旅(12)cmake之旅(12):CPack 打包与发布1 CPack 是什么2 最简单的 CPack 配置3 配置 CPack3.1 基本信息3.2 选择打包格式4 生成 DEB 包5 生成 RPM 包6 完整示例7 组件化打包8 source 包9 本篇命令速查表10 总结与…...
MindSpore 环境配置完全指南奄
前面我们对 Kafka 的整体架构和一些关键的概念有了一个基本的认知,本文主要介绍 Kafka 的一些配置参数。掌握这些参数的作用对我们的运维和调优工作还是非常有帮助的。 写在前面 Kafka 作为一个成熟的事件流平台,有非常多的配置参数。详细的参数列表可以…...
