gin 默认表单大小限制为32mb且无超时控制,上传大视频易超时或报400错误;需在gin.default()后设置r.maxmultipartmemory=1024(单位mb)并配置http超时。

上传大视频文件时 Bind 一直超时或报 400 Bad Request
Gin 默认限制表单大小(maxMemory=32MB),且不设超时控制,上传几百 MB 的视频很容易卡死或被中间件(如 Nginx)提前断连。必须显式配置解析行为和 HTTP 超时。
- 在
gin.Default()后立即调用r.MaxMultipartMemory = 1024 (例如设为 1GB),避免 <code>Bind阶段内存不足 - 禁用默认的
Bind,改用c.FormFile("video")手动获取文件句柄,避免 Gin 尝试解析整个 multipart body 到内存 - 用
http.Server包裹 Gin 引擎,设置ReadTimeout和WriteTimeout(至少 5–10 分钟),否则上传中途会因连接关闭而失败 - 若前端走 Nginx,务必加
client_max_body_size 2g;和proxy_read_timeout 600;,否则请求根本到不了 Gin
转码任务不能阻塞 HTTP 请求,必须异步触发
FFmpeg 转码耗时长、吃 CPU,直接在 HTTP handler 里调用 exec.Command 会导致协程卡住、并发数暴跌,还可能因超时返回错误响应。必须解耦上传与处理流程。
- 上传成功后,只把文件路径、用户 ID、目标格式等写入消息队列(如 Redis List 或 Kafka),立刻返回
202 Accepted+ 任务 ID - 单独起一个后台 goroutine(或独立 worker 进程)监听队列,取出任务后调用
exec.Command("ffmpeg", "-i", src, "-c:v", "libx264", dst) - 转码完成后,用 Redis Pub/Sub 或数据库状态更新通知前端;不要在 handler 里轮询 FFmpeg 进程状态
- 注意:
ffmpeg命令需预装在运行环境,且确保工作目录可读写;建议用绝对路径调用,避免exec: "ffmpeg": executable file not found
怎么安全地保存上传的视频并防止覆盖或路径遍历
直接用 c.PostForm("filename") 拼接路径是高危操作,攻击者可传 ../../../etc/passwd 触发任意文件写入。
- 永远忽略客户端传来的原始文件名,用
uuid.New().String() + ".mp4"生成存储名 - 保存路径固定在某个子目录(如
./uploads/),用filepath.Join(uploadDir, safeName)构造,再通过filepath.Abs+strings.HasPrefix校验是否仍在允许目录内 - 上传前检查 MIME 类型(
file.Header.Get("Content-Type")),仅接受video/mp4、video/quicktime等合法类型,避免恶意伪装 - Linux 下记得
chown上传目录给运行用户,否则 FFmpeg 可能因权限失败
前端怎么知道转码完成?别用轮询,用 SSE 或 WebSocket
HTTP 轮询浪费连接、延迟高、服务端压力大。Gin 支持原生 SSE(Server-Sent Events),轻量且兼容性好,适合状态推送。
- 上传返回时附带唯一
task_id,前端用EventSource订阅/status?id=xxx - 后端用
c.SSEvent("progress", data)推送进度(如{"status":"processing","percent":35}),用c.SSEvent("done", result)标记完成 - 注意设置
c.Writer.Header().Set("Cache-Control", "no-cache")和c.Writer.Header().Set("Connection", "keep-alive"),否则某些代理会缓存或关闭长连接 - 如果需要双向实时交互(如取消任务),才考虑 WebSocket;SSE 对单向通知已足够,实现更简单











