应手动设限并流式处理:用 http.maxbytesreader 包裹 r.body 限制请求体大小(如2gb),再调用 r.parsemultipartform(32

Go 服务端如何接收超大文件而不崩溃
直接用 http.Request.Body 读整个文件到内存,1GB 文件就可能吃光服务内存、触发 OOM。Go 默认的 http.MaxBytesReader 也不拦得住恶意上传——它只限制响应体,不约束请求体大小。必须手动设限 + 流式处理。
- 在
http.HandleFunc或路由前,用http.MaxBytesReader包裹r.Body,例如限制单次上传 ≤2GB:limitReader := http.MaxBytesReader(w, r.Body, 2*1024*1024*1024)
- 立刻调用
r.ParseMultipartForm(32 (32MB 内存缓冲),避免表单解析把所有字段全载入内存;超出部分自动落盘到 <code>os.TempDir() - 用
multipart.Reader配合io.Copy直接流式写入磁盘或对象存储,跳过bytes.Buffer或io.ReadAll
前端传来的分片怎么在 Go 后端拼接还原
分片上传不是靠“名字顺序”拼接,而是依赖前端明确传递的 chunkIndex、totalChunks、identifier(如文件 hash)这三个关键字段。后端不做校验就盲拼,极易错序或混件。
- 每个分片请求必须带
X-Upload-Id(或 POST body 中的upload_id)作为本次上传会话唯一标识 - 用
sync.Map或 Redis 缓存该 upload_id 对应的已接收分片索引集合,收到新分片时先查是否重复、是否越界 - 拼接时按
chunkIndex升序打开各临时分片文件,用io.MultiReader组装,再io.Copy到目标文件,不要用os.WriteAt手动 seek —— 容易因分片大小不均出错 - 最后校验合并后文件的
sha256.Sum256是否与前端声明的fileHash一致
为什么用了分片,上传还经常卡在 99% 就断连
这不是 Go 代码问题,而是 HTTP 连接被中间设备(Nginx、ALB、防火墙)强制中断。默认 60 秒超时对大文件上传完全不够用,且分片请求若未携带明确的 Content-Length,某些代理会拒绝转发。
- Nginx 需显式配置:
client_max_body_size 4G;<br>proxy_read_timeout 3600;<br>proxy_send_timeout 3600;<br>proxy_buffering off;
- Go 服务启动时设置
http.Server.ReadTimeout和WriteTimeout至少 1 小时,别依赖默认值 - 前端每个分片请求必须带准确的
Content-Lengthheader,禁用 chunked encoding;用fetch时确保body是Blob或ArrayBuffer,而非FormData(后者会加 boundary,破坏长度确定性)
上传完成后的清理逻辑容易漏哪几处
临时分片文件、Redis 中的上传元数据、失败重试留下的孤儿块——这些不主动删,磁盘和内存几天就爆掉。
- 成功合并后,用
os.RemoveAll删除整个分片目录,别只删单个文件 - 给 Redis 的 upload_id key 设置 TTL(比如 24 小时),但注意:TTL 不是删除时机,只是兜底;应在合并成功后立即
DEL,避免误删正在上传的其他会话 - 加一个后台 goroutine 定期扫描
/tmp/uploads/下超过 2 小时未修改的目录并清理,防止 panic 导致清理逻辑没执行 - 如果用了对象存储(如 S3),分片上传应走
CreateMultipartUpload → UploadPart → CompleteMultipartUpload流程,而不是本地拼完再 PUT —— 否则失去断点续传和并发上传优势
分片上传真正的复杂点不在 Go 怎么读写,而在跨网络、跨组件的状态一致性。upload_id 冲突、分片丢失、客户端重试幂等、清理时机竞态——这些才是压测时突然崩掉的原因。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











