当前位置: 首页 > news >正文

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源码实现使用多线程并发下载大文件的功能

摘要&#xff1a;Go语言编码实现了使用多线程并发下载文件的功能。 1. 代码流程介绍 1. 获取系统的CPU核心数量&#xff0c;并将其作为线程数的参考值&#xff0c;并打印出来。 2. 定义要下载的文件的URL、线程数和输出文件名。 3. 使用getFileSize()函数获取文件大小&#xf…...

Python基础入门例程1-NP1 Hello World!

描述 将字符串 Hello World! 存储到变量str中&#xff0c;再使用print语句将其打印出来。 输入描述&#xff1a; 无 输出描述&#xff1a; 一行输出字符串Hello World! 解答&#xff1a; str "Hello World!" print(str) 解释说明&#xff1a; 赋值变量&…...

前端面试题10.23

解决的最复杂的前端问题&#xff0c;介绍一下 最复杂的前端问题之一是浏览器兼容性&#xff0c;不同浏览器对网页的渲染方式存在差异&#xff0c;需要针对不同浏览器做兼容性处理。此外&#xff0c;前端性能优化也是一个复杂的问题&#xff0c;需要综合考虑网页加载速度、渲染…...

DYC算法开发与测试(基于ModelBase实现)

ModelBase是经纬恒润开发的车辆仿真软件&#xff0c;包含两个大版本&#xff1a;动力学版本、智能驾驶版本。动力学版包含高精度动力学模型&#xff0c;能很好地复现车辆在实际道路中运行的各种状态变化&#xff0c;可用于乘用车、商用车动力底盘系统算法开发、控制器仿真测试&…...

第四章 路由基础

目录 4.1 路由器概述 4.1.1 路由器定义 4.1.2 路由器工作原理 4.1.3 路由表的生成方式 &#xff08;1&#xff09;直连路由 &#xff08;2&#xff09;静态路由 &#xff08;3&#xff09;动态路由 4.1.4 路由器的接口 &#xff08;1&#xff09;配置接口 &#xff0…...

Java逻辑运算符(、||和!),Java关系运算符

逻辑运算符把各个运算的关系表达式连接起来组成一个复杂的逻辑表达式&#xff0c;以判断程序中的表达式是否成立&#xff0c;判断的结果是 true 或 false。 逻辑运算符是对布尔型变量进行运算&#xff0c;其结果也是布尔型&#xff0c;具体如表 1 所示。 表 1 逻辑运算符的用…...

三个设备文件

...

Java赋值运算符(=)

赋值运算符是指为变量或常量指定数值的符号。赋值运算符的符号为“”&#xff0c;它是双目运算符&#xff0c;左边的操作数必须是变量&#xff0c;不能是常量或表达式。 其语法格式如下所示&#xff1a; 变量名称表达式内容 在 Java 语言中&#xff0c;“变量名称”和“表达式…...

提升药店效率:山海鲸医药零售大屏的成功案例

在医药行业中&#xff0c;特别是医药零售领域&#xff0c;高效的药品管理和客户服务至关重要。随着科技的飞速发展&#xff0c;数字化解决方案已经成为提高医药零售管控效率的有效工具之一。其中&#xff0c;医药零售管控大屏作为一种强大的工具&#xff0c;正在以独特的方式改…...

使用Fragement(碎片)

一、Fragment简介 屏幕大小的差距可能会使同样的界面在不同设备上显示出不同的效果&#xff0c;为了能同时兼顾到手机和平板电脑的开发&#xff0c;从Android3.0版本开始提供了Fragment。 Fragment&#xff08;碎片&#xff09;是一种嵌入在Activity中的UI片段&#xff0c;它…...

种花问题(Python题目)

假设有一个很长的花坛&#xff0c;一部分地块种植了花&#xff0c;另一部分却没有。可是&#xff0c;花不能种植在相邻的地块上&#xff0c;它们会争夺水源&#xff0c;两者都会死去。 给你一个整数数组 flowerbed 表示花坛&#xff0c;由若干 0 和 1 组成&#xff0c;其中 0 …...

STM32入门F4

学习资料&#xff1a;杨桃电子&#xff0c;官网&#xff1a;洋桃电子 | 杜洋工作室 www.doyoung.net 嵌入式开发核心宗旨&#xff1a;以最适合的性能、功能、成本来完成最有性价比的产品开发。 1.为什么要学F407 STM32F103系列与STM32F407系列对照表&#xff1a; 2.F4系列命…...

【30】c++设计模式——>状态模式

状态模式概述 状态模式是一种行为型设计模式&#xff0c;它可以让一个对象在其内部状态发生变化时更改其行为。通过将每个状态封装成一个独立的类&#xff0c;我们可以使状态之间互相独立&#xff0c;并且使得状态的切换变得更加灵活、可扩展。&#xff08;多个状态之间可以相…...

中文编程开发语言编程实际案例:程序控制灯电路以及桌球台球室用这个程序计时计费

中文编程开发语言编程实际案例&#xff1a;程序控制灯电路以及桌球台球室用这个程序计时计费 上图为&#xff1a;程序控制的硬件设备电路图 上图为&#xff1a;程序控制灯的开关软件截图&#xff0c;适用范围比如&#xff1a;台球厅桌球室的计时计费管理&#xff0c;计时的时候…...

【python】高斯日记

题目&#xff1a; """ 题目描述&#xff1a; 高斯出生于1777年4月30日&#xff0c;记作1777-4-30。在此基础上&#xff0c;我们希望你写一个程序&#xff0c;给定一个数字n&#xff0c;表示从高斯出生的那天算起的第n天&#xff0c;输出这一天的具体日期。例如&…...

[论文笔记]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风格

本章内容包括&#xff1a; 了解Power BI可以处理的不同类型的数据了解您的商业智能工具选项熟悉Power BI术语 数据无处不在。从你醒来的那一刻到你睡觉的时候&#xff0c;某个系统会代表你收集数据。即使在你睡觉的时候&#xff0c;也会产生与你生活的某些方面相关的数据。如…...

【C++和数据结构】位图和布隆过滤器

目录 一、位图 1、位图的概念 2、位图的实现 ①、基本结构 ②、set ③、reset&#xff1a; ④、test ⑤、问题&#xff1a; ⑥、位图优缺点及应用&#xff1a; ⑦、完整代码及测试 二、布隆过滤器 1、布隆过滤器的提出 2、布隆过滤器的实现 ①、基本结构 ②…...

Mybatis分页

本文主要讲解Mybatis分页相关的技术分享&#xff0c;如果觉得不错的话&#xff0c;就点个赞吧。。。。 Mybatis分页主要有2种类型&#xff1a; 一、物理分页&#xff1a; 1、定义&#xff1a; 物理分页是在数据库层面进行的分页&#xff0c;即通过SQL语句直接从数据库中查询…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...