c.File()不能用于音视频流式传输,因其将文件全量读入内存,导致OOM、无断点续传、不支持动态生成和流控;应改用io.Pipe或分块Copy配合手动Header控制。

为什么 c.File() 不能直接用于音视频流式传输
因为 c.File() 会把整个文件一次性读入内存再发送,对几十 MB 的 MP4 或几小时的直播流来说,极易触发 OOM;它也不支持断点续传、Range 请求、动态生成(如实时转码),更无法控制缓冲节奏。
- 实际现象:100MB 视频响应延迟 >8s,内存峰值飙升至 2GB+,并发 20 就卡死
- 根本原因:Gin 默认用
http.ServeContent实现c.File(),底层依赖stat+ 全量io.Copy,无流控、无 chunk 分发能力 - 替代路径:必须绕过
c.File(),直接操作c.Writer和底层http.ResponseWriter,配合io.Pipe或分块io.CopyN
如何用 io.Pipe 实现低延迟音视频流
适用于需要实时吐出数据的场景,比如边转码边推流、摄像头 RTSP 转 HTTP-FLV、或 WebRTC 前置代理。核心是让生产者(编码器/采集器)和消费者(HTTP 连接)异步解耦,避免阻塞。
- 必须禁用 Gin 的自动 header 写入:调用
c.Writer.WriteHeader(http.StatusOK)前,先手动设置Content-Type(如"video/mp4"或"application/x-mpegURL")和Transfer-Encoding: chunked -
io.Pipe()的写端交给 encoder goroutine,读端交给io.Copy(c.Writer, pipeReader),注意加defer pipeWriter.Close() - 关键细节:若 encoder 可能 panic,需用
recover捕获并显式pipeWriter.CloseWithError(),否则客户端 hang 住
支持 Range 请求的音视频断点续传怎么写
浏览器播放器(如 video 标签)默认发 Range: bytes=0-1023 请求,不支持则无法拖动进度条、无法暂停后继续播。
- 必须解析
c.Request.Header.Get("Range"),匹配正则^bytes=(\d+)-(\d*)$,提取 start 和可选 end - 用
os.OpenFile(filePath, os.O_RDONLY, 0)打开只读句柄,file.Seek(start, io.SeekStart)定位,再用io.CopyN(c.Writer, file, length)精确输出字节数 - 响应头必须包含:
Content-Range(格式"bytes 0-1023/12345678")、Accept-Ranges: bytes、Status: 206 Partial Content - 容易漏掉:没校验
start < fileSize,导致Seek失败返回 500;或没设Content-Length导致 chunked 编码干扰播放器解析
大文件上传时怎么避免音视频被截断或损坏
用户上传 MP4/AVI 时,c.FormFile() 会把整个文件载入内存再返回 *multipart.FileHeader,几百 MB 就可能 OOM;且 multipart 解析本身会消耗 CPU,影响实时性。
- 正确做法:禁用自动 multipart 解析(不调用
r.MaxMultipartMemory或设为 0),改用c.Request.MultipartReader()获取multipart.Reader - 循环调用
reader.NextPart(),检查part.FormName() == "video"后,直接io.Copy(dstWriter, part)流式写入磁盘或对象存储,全程零内存缓存 - 必须验证:在
part中读取前几个字节判断 magic number(如 MP4 的ftyp、AVI 的RIFF),防止恶意伪造文件头 - 注意边界:
MultipartReader不处理 boundary 外的垃圾数据,若前端 SDK 发送格式异常,需加io.LimitReader(part, maxFileSize)防 DoS


















