Flutter:如何使用http包分块下载大文件并写入本地?
Go 基于http包实现大文件分块下载与合并
核心原理
利用HTTP协议的Range请求头,向服务器请求文件的指定字节区间,将大文件拆分为多个小分块并行/串行下载,最终将各分块写入目标文件的对应偏移位置,无需额外合并操作。
实现步骤与代码示例
1. 前置检查:确认服务器支持分块下载
先发送HEAD请求,获取文件总大小,并验证服务器是否支持Range请求:
import ( "fmt" "net/http" "strconv" ) func getFileInfo(url string) (int64, bool, error) { resp, err := http.Head(url) if err != nil { return 0, false, err } defer resp.Body.Close() // 检查服务器是否支持Range分段请求 acceptRanges := resp.Header.Get("Accept-Ranges") == "bytes" if !acceptRanges { return 0, false, fmt.Errorf("server does not support range requests") } // 解析文件总大小 contentLengthStr := resp.Header.Get("Content-Length") contentLength, err := strconv.ParseInt(contentLengthStr, 10, 64) if err != nil { return 0, false, err } return contentLength, acceptRanges, nil }
2. 分块规划与并发下载
定义分块大小(示例为10MB),计算每个分块的起始/结束字节,通过goroutine并发下载,同时控制并发数避免请求过载,下载的分块直接写入目标文件的对应偏移位置:
import ( "fmt" "io" "net/http" "os" "strconv" "sync" ) const chunkSize = 10 * 1024 * 1024 // 10MB每块 func downloadChunk(url string, start, end int64, file *os.File, wg *sync.WaitGroup, sem chan struct{}) { defer wg.Done() defer func() { <-sem }() // 释放并发信号量 // 构建带Range头的请求 req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Printf("chunk %d-%d: failed to create request: %v\n", start, end, err) return } req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", start, end)) resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Printf("chunk %d-%d: failed to download: %v\n", start, end, err) return } defer resp.Body.Close() // 将分块写入文件的指定偏移位置 _, err = io.CopyN(file, resp.Body, end-start+1) if err != nil { fmt.Printf("chunk %d-%d: failed to write to file: %v\n", start, end, err) return } } func downloadLargeFile(url, outputPath string) error { contentLength, supportsRange, err := getFileInfo(url) if err != nil { // 若服务器不支持分块,退化为普通单线程下载 if !supportsRange { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() file, err := os.Create(outputPath) if err != nil { return err } defer file.Close() _, err = io.Copy(file, resp.Body) return err } return err } // 创建目标文件并预分配磁盘空间(提升写入效率) file, err := os.Create(outputPath) if err != nil { return err } defer file.Close() if err := file.Truncate(contentLength); err != nil { return err } var wg sync.WaitGroup sem := make(chan struct{}, 5) // 限制并发数为5,可根据服务器配置调整 // 遍历所有分块,启动goroutine下载 for start := int64(0); start < contentLength; start += chunkSize { end := start + chunkSize - 1 if end >= contentLength { end = contentLength - 1 } wg.Add(1) sem <- struct{}{} // 获取并发信号量 go downloadChunk(url, start, end, file, &wg, sem) } wg.Wait() return nil }
3. 关键注意事项
- 并发数控制:避免无限制创建goroutine,建议设置5-10的并发数,防止触发服务器限流或本地资源耗尽。
- 重试机制:可给
downloadChunk函数添加重试逻辑,处理临时网络波动导致的下载失败。 - 断点续传:若需要支持断点,可将已下载的分块范围记录到本地文件,下次启动时跳过已完成的块。
- 校验完整性:下载完成后可通过MD5/SHA等哈希值校验文件完整性。
内容的提问来源于stack exchange,提问作者May
相关产品推荐
相关产品推荐

