You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 06:45:31