如何通过CGI实现HTTP实时传输生成中的文件?(yt-dlp场景)
Apache/CGI + yt-dlp 流式传输(Chunked编码)解决方案
先明确:Apache CGI本身支持chunked传输,但坑主要在输出缓冲和脚本的流式处理逻辑上,以下是亲测有效的配置和代码:
1. 先搞定Apache的CGI配置
Apache默认会缓冲CGI脚本的输出,这直接卡死流式传输,必须先关缓冲:
- 在
httpd.conf或者站点目录的.htaccess里加这些配置:# 禁用gzip压缩(避免干扰二进制流的chunked结构) SetEnv no-gzip 1 SetEnv dont-vary 1 # 关闭CGI输出缓冲(如果用PHP的话单独加下面这句) php_flag output_buffering off - 确认
mod_cgi或mod_cgid模块已经启用(Apache默认一般开着,没开的话用a2enmod cgi启用)。
2. 写对CGI脚本(拿Python举例)
核心是关掉脚本自身的输出缓冲,然后把yt-dlp的输出实时拆成chunked格式发出去,不能等整个视频下完再输出。
示例脚本youtube_download.cgi(记得给执行权限:chmod +x youtube_download.cgi):
#!/usr/bin/env python3 import subprocess import sys # 先发送响应头,必须用二进制输出避免编码问题 sys.stdout.buffer.write(b"Transfer-Encoding: chunked\r\n") # 这里的Content-Type要对应视频格式,比如mp4就用video/mp4,webm用video/webm sys.stdout.buffer.write(b"Content-Type: video/mp4\r\n") sys.stdout.buffer.write(b"\r\n") # 头部结束的空行 sys.stdout.buffer.flush() # 立即把头部发出去,不能等 # 调用yt-dlp,直接输出到stdout cmd = [ "yt-dlp", "-f", "bestvideo+bestaudio/best", # 选你要的格式,按需调整 "-o", "-", # 关键:输出到stdout # 这里换成用户请求的视频URL,实际场景要从CGI的QUERY_STRING里取 "https://www.youtube.com/watch?v=example" ] # 启动yt-dlp进程,实时读输出 with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL) as proc: while True: # 每次读4KB的块,大小可以自己调 chunk = proc.stdout.read(4096) if not chunk: # 读到空说明yt-dlp输出完了 break # 按照chunked编码规则输出:十六进制长度 + 换行 + 内容 + 换行 sys.stdout.buffer.write(f"{len(chunk):x}\r\n".encode()) sys.stdout.buffer.write(chunk) sys.stdout.buffer.write(b"\r\n") sys.stdout.buffer.flush() # 实时发走,别缓冲 # 发送chunked结束标记 sys.stdout.buffer.write(b"0\r\n\r\n") sys.stdout.buffer.flush()
3. 踩过的坑排查
- 无限传输:大概率是脚本没检测到yt-dlp的进程结束,比如死循环了。确保读到
proc.stdout.read()返回空就终止循环,同时检查yt-dlp会不会卡住(比如终端直接跑命令看能不能正常结束)。 - 内容为空:
- 先在终端跑一遍脚本里的yt-dlp命令,看有没有输出,要是终端都没输出,那就是yt-dlp的参数错了。
- 一定要加
flush(),不管是头部还是每个分块,不然Python会把输出攒着不发,Apache也会等缓冲区满了才传。 - 别开gzip压缩,压缩会把chunked的结构搞乱,直接导致传输异常。
- 权限问题:Apache的运行用户(比如www-data)得能执行yt-dlp,还要能访问ffmpeg(如果yt-dlp需要合并音视频的话),不然会悄悄失败。
内容的提问来源于stack exchange,提问作者Jen
相关产品推荐
相关产品推荐

