Rails:如何在Application Controller中实现Socket劫持传输视频流
Rails控制器实现实时视频流反向代理(边接收边传输)
核心问题解决:ActionController::Live边读边发
你之前遇到的"先加载所有数据再发送"问题,是因为没有用流式读取远程响应的方式。下面是正确的控制器实现,能做到边接收远程视频块,边实时转发给客户端:
class VideoProxyController < ApplicationController include ActionController::Live def stream # 根据你的视频类型设置正确的Content-Type response.headers['Content-Type'] = 'video/mp4' # 开启分块传输,告知客户端数据会流式到达 response.headers['Transfer-Encoding'] = 'chunked' # 禁用缓存,避免客户端缓存不完整的流数据 response.headers['Cache-Control'] = 'no-cache' # 替换为你的实际远程视频地址 remote_video_uri = URI('https://your-remote-server.com/video-stream') # 流式请求远程视频并转发 Net::HTTP.start(remote_video_uri.host, remote_video_uri.port, use_ssl: remote_video_uri.scheme == 'https') do |http| http.request_get(remote_video_uri.request_uri) do |remote_res| # 分块读取远程响应,每块大小可根据需求调整(这里是1MB) remote_res.read_body(1024*1024) do |chunk| response.stream.write(chunk) # 强制刷新输出缓冲区,确保数据即时发送给客户端 response.stream.flush end end end rescue IOError # 客户端主动断开连接时会触发此错误,无需额外处理 ensure # 必须关闭流,防止Puma连接泄漏 response.stream.close end end
关键注意事项
- 必须启用ActionController::Live:这是Rails提供的流式响应API,Puma原生支持该特性,无需额外配置。
- 分块读取远程响应:使用
remote_res.read_body带块参数的方式,会逐块读取远程数据,而不是一次性加载到内存。 - 响应头设置:
Transfer-Encoding: chunked是流式传输的核心头,必须设置;Content-Type要匹配视频的实际格式(如video/mp4、video/avi等)。 - 流的关闭:在
ensure块中关闭response.stream,避免Puma的连接资源泄漏。
从config.ru迁移到控制器的正确方式
config.ru中直接操作底层Socket的方式在控制器中不适用,因为Rails对请求/响应做了封装。改用ActionController::Live的response.stream是官方推荐的方案,同时可以轻松集成业务逻辑:
- 在
stream方法开头添加权限校验、用户认证等逻辑 - 记录视频流的访问日志、统计数据
- 根据业务规则动态替换远程视频地址
关于空响应的问题
之前添加:head ok是错误的——head方法只会返回响应头,不会发送响应体。只要按照上面的代码正确设置响应头并写入response.stream,就能正常返回流式数据。
Sendfile的适用性
sendfile gem主要用于本地文件的零拷贝传输(让操作系统直接把文件内容发送给客户端,跳过应用层),但对于远程视频流,数据需要先从远程服务器传输到Rails服务器,再转发给客户端,无法使用sendfile的零拷贝特性,因此流式读取转发是更合适的方案。
内容的提问来源于stack exchange,提问作者korncola
相关产品推荐
相关产品推荐

