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

这篇具有很好参考价值的文章主要介绍了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 main

import (
	"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 := numCPU
	fmt.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.WaitGroup
	wg.Add(threads)

	// 创建一个通道用于接收下载完成的文件块路径
	chunkPaths := make(chan string, threads)

	// 启动多个 goroutine 并发下载文件块
	for i := 0; i < threads; i++ {
		go func(index int) {
			defer wg.Done()

			start := int64(index) * chunkSize
			end := 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
文件下载完成。文章来源地址https://www.toymoban.com/news/detail-719888.html

到了这里,关于Go源码实现使用多线程并发下载大文件的功能的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用JFLASH实现文件程序自动化合并及下载功能

    主要总结下使用 SEGGER 工具集的 JFLASH 软件实现 hex/bin 文件合并以及程序的自动下载使用方法。 起因是最近使用到LVGL字库文件的制作,每次都要将分散的bin文件按既定分配的偏移作合并处理,刚开始使用的是二进制文件合并工具,文件少的时候还行,但是文件一多,一个个的添

    2024年01月21日
    浏览(41)
  • 从源码分析 Go 语言使用 cgo 导致的线程增长

    TDengine Go 连接器 https://github.com/taosdata/driver-go 使用 cgo 调用 taos.so 中的 API,使用过程中发现线程数不断增长,本文从一个 cgo 调用开始解析 Go 源码,分析造成线程增长的原因。 对 driver-go/wrapper/taosc.go 进行转换 go tool cgo taosc.go 执行后生成 _obj 文件夹 以 taosc.cgo1.go 中 TaosResetC

    2024年02月07日
    浏览(53)
  • 基于 HTTP Range 实现文件分片并发下载!

    目录 前言 基础下载功能 进阶下载功能 单片下载 多片下载  浏览器发送预检(preflight)请求 express 不支持多段 range multipart/** 搭配 boundary=** 分片下载功能 “只读的” ArrayBuffer 对象 DataView 子类 Uint8Array 操作二进制数据 Blob + createObjectURL 创建 url 全部代码 通用的文件分片下载

    2024年02月10日
    浏览(55)
  • go中数组、切片、map是否线程(并发)安全?

    博客主页:🏆 看看是李XX还是李歘歘  🏆 🌺每天不定期分享一些包括但不限于计算机基础、算法、后端开发相关的知识点,以及职场小菜鸡的生活。🌺 💗 点关注不迷路,总有一些📖知识点📖是你想要的 💗  目录 什么是线程(并发)安全? 非线程安全原因 map 解决方案

    2024年02月02日
    浏览(54)
  • Go语言程序设计-第9章--使用共享变量实现并发

    一个能在串行程序中正确工作的函数。如果这个函数在并发调用时仍然能正确工作,那么这个函数是并发安全的。在这里并发调用是指,在没有额外同步机制的情况下,从两个或者多个 goroutine 同时调用这个函数。如果一个类型的所有可访问方法和操作都是并发安全时,则它

    2024年02月02日
    浏览(65)
  • uniapp - 【全端兼容】实现保存视频到手机相册功能,将 mp4 在线视频下载并存储到用户的手机中,uniapp App h5 小程序将视频文件下载保存(详细示例源码及注释一键复制,开箱即用!)

    在uniapp开发中,实现安卓苹果app、h5网页网站、小程序保存视频到相册功能,点击保存按钮后下载视频并将其存储到用户的手机相册中,完整示例源码及注释,新手小白开箱即用! 直接复制代码,稍微改下就能用到你的项目中去了(保证可用)。 可复制运行,或按需复制。

    2024年02月09日
    浏览(155)
  • 【前端面试】中大文件上传/下载:中等文件代理服务器放行+大文件切片传输+并发请求+localstorage实现断点续传

    目录 切片上传~spark-md5 原理:流式计算+分块处理 文件标识spark-md5:A-B A.切片哈希值合并 B.首尾切片+其他切片前中后各取2M 计算hash:A-B(参考React的Fiber架构) A.线程:web-worker B.空闲:requestIdleCallback 异步并发控制:A-B(参考http2的多路复用) A.promise.allSettled() B.并发数max=

    2024年02月12日
    浏览(55)
  • 前端实现文件下载功能——文件流

    前端下载文件一般使用的是blob 核心的步骤是获取后端响应的文件流,用blob创建一个临时的URL,然后创建一个隐藏的a标签,实现下载需求。 那就先上代码 如果后端响应的数据是一个二进制数据,那我们就得这是响应类型是blob,否则浏览器会默认按照json解析 至于后端如何向

    2024年02月11日
    浏览(47)
  • 基于curl 使用http多线程下载大文件

    如需完整代码,可评论区留言

    2024年02月04日
    浏览(58)
  • ajax实现下载文件功能

    最近在做下载功能,因为后端需要生成PDF并上传到云服务器,只返回文件的URL,所以百度了很多找到了一个简单的方法. Blob(Binary Large Object)表示二进制类型的大对象。在数据库管理系统中,将二进制数据存储为一个单一个体的集合。Blob 通常是影像、声音或多媒体文件。在 Ja

    2024年02月15日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包