Go大文件上传下载需流式处理:禁用ParseMultipartForm、URL传元信息、io.LimitReader截取分片、Blob.slice零拷贝切片、Range流式下载、排序校验哈希后合并。

Go语言处理大文件上传与分片下载,关键不在“写多复杂”,而在“绕开默认陷阱、控制流与状态”。标准库的 ParseMultipartForm 会把整个请求体读进内存,对大文件就是硬伤;而下载若用 os.ReadFile 全量加载,同样不可行。真正优雅的做法是:流式读写 + 显式分片控制 + 状态可恢复。
上传必须禁用自动 multipart 解析
HTTP handler 一进来就调 r.ParseMultipartForm(0)(传 0 表示跳过内存缓存),否则 r.Body 被提前消费,后续无法流式读取分片数据。这不是性能配置问题,是行为级限制。
- 元信息(如
uploadId、chunkIndex、totalChunks)统一从 URL 查询参数或r.FormValue()获取,别碰r.MultipartForm - 二进制分片数据直接从
r.Body流读,用io.LimitReader(r.Body, chunkSize)精确截断,避免越界 - 写入临时分片时用
os.O_CREATE | os.O_WRONLY | os.O_EXCL,防止并发写同一序号导致内容覆盖或错乱
前端切片要零拷贝,不能全量读再分
浏览器端绝不能用 FileReader.readAsArrayBuffer 把几 GB 文件一次性读进内存——UI 卡死、GC 压力大、甚至 OOM。正确方式是用 Blob.slice(start, end) 直接生成子 Blob,不复制原始数据,内存占用恒定。
- 每个分片发独立 POST 请求,URL 带路径参数(如
/upload/abc123/chunk?index=5) - Header 必须设为
Content-Type: application/octet-stream;若误设multipart/form-data却没带boundary,服务端会直接 400 - 建议加
X-Chunk-Hash头(如 SHA256 前 8 字节),服务端边写边校验,不落地再算
分片下载需支持 Range 和流式响应
下载端不拼接完整文件,而是按需返回字节段。用 http.ServeContent 或手动处理 Range 头,配合 io.CopyN 或 io.MultiWriter 控制输出。
立即学习“go语言免费学习笔记(深入)”;
- 打开文件用
os.OpenFile(path, os.O_RDONLY, 0),避免锁或权限问题 - 读取前先
stat获取总大小,校验Range合法性(如bytes=0-1023不能超文件长度) - 响应头必须包含
Content-Range、Accept-Ranges: bytes、Content-Length,让客户端能续传
合并与校验不能只数文件个数
所有分片上传完后,服务端合并前必须做两件事:按 chunkIndex 数值排序(不是字符串排序!chunk_10 必须排在 chunk_2 后),且逐片重算哈希并比对客户端所传 X-Chunk-Hash。
- 合并时用
os.OpenFile(..., os.O_CREATE|os.O_WRONLY)创建目标文件,然后遍历有序分片列表,io.Copy追加写入 - 合并完成后,对最终文件整体计算一次 SHA256,和初始化时客户端上报的文件总哈希比对,不一致则删掉并报错
- 成功后清理临时分片目录,但保留元数据(如 uploadId → final filename 映射)供审计或重试查证


















