基于Sidekiq+Redis的FFMPEG视频转码后台任务进度前端展示咨询
解决方案:基于Sidekiq+Redis实现转码进度实时展示
刚好之前做过类似的视频转码服务,用的也是Sidekiq+Streamio FFMPEG,给你梳理下我实际落地的方案,应该能完美解决你的问题。
一、进度值的存储方案(复用Redis)
因为你已经在用Sidekiq,而Sidekiq本身依赖Redis,所以直接用Redis来存储进度值是最省心的选择,不需要额外引入其他存储组件。
核心思路:
- 利用Sidekiq任务自带的唯一
job_id(通过self.jid获取)作为Redis的键前缀,比如transcode_progress:{job_id} - 在Streamio的进度回调中,实时计算转码百分比并写入Redis
- 给Redis键设置合理的过期时间(比如24小时),避免无用数据占用内存
Streamio进度回调示例:
Streamio的Transcoder类提供了on_progress方法,每次转码进度更新时都会触发这个回调,回调参数是0-1之间的浮点数(0代表未开始,1代表完成):
class TranscodeJob include Sidekiq::Job def perform(video_id) job_id = self.jid # 获取当前Sidekiq任务的唯一ID video = Video.find(video_id) transcoder = Streamio::FFMPEG::Transcoder.new(video.input_path, video.output_path) # 绑定进度回调 transcoder.on_progress do |progress_float| # 转成百分比并保留1位小数 progress_percent = (progress_float * 100).round(1) # 写入Redis,设置24小时过期 Redis.current.setex("transcode_progress:#{job_id}", 86400, progress_percent) # 同时存储任务状态为"running" Redis.current.setex("transcode_status:#{job_id}", 86400, "running") end begin # 执行转码 transcoder.transcode # 转码完成后,更新进度为100%,状态为成功 Redis.current.setex("transcode_progress:#{job_id}", 86400, 100) Redis.current.setex("transcode_status:#{job_id}", 86400, "success") rescue StandardError => e # 转码失败时更新状态和错误信息 Redis.current.setex("transcode_status:#{job_id}", 86400, "failed") Redis.current.setex("transcode_error:#{job_id}", 86400, e.message) end end end
二、前端获取进度的实现方式
前端主要通过定时轮询后端接口来获取进度(如果是大型应用也可以用WebSocket,但轮询实现更简单,适合中小规模场景)。
步骤:
- 用户提交转码请求后,后端启动Sidekiq任务,并将
job_id返回给前端 - 前端用这个
job_id每隔1-2秒发送一次请求到后端的进度查询接口 - 后端接口从Redis中读取对应
job_id的进度和状态,返回给前端 - 前端根据返回的数据更新页面上的进度条,当状态为
success或failed时停止轮询
后端进度查询接口示例(以Rails为例):
class TranscodeProgressController < ApplicationController def show job_id = params[:job_id] # 从Redis读取数据 progress = Redis.current.get("transcode_progress:#{job_id}") status = Redis.current.get("transcode_status:#{job_id}") error = Redis.current.get("transcode_error:#{job_id}") # 返回JSON格式数据 render json: { progress: progress ? progress.to_f : nil, status: status || "pending", error: error } end end
前端轮询示例(原生JS):
function startProgressPolling(jobId) { // 设置1.5秒轮询一次,平衡实时性和服务器压力 const pollInterval = setInterval(async () => { try { const response = await fetch(`/transcode_progress/${jobId}`); const data = await response.json(); // 更新页面进度条(假设页面有id为progress-bar的元素) if (data.progress !== null) { const progressBar = document.getElementById('progress-bar'); progressBar.style.width = `${data.progress}%`; progressBar.textContent = `${data.progress}%`; } // 任务完成或失败时停止轮询 if (data.status === 'success') { clearInterval(pollInterval); alert('视频转码完成!'); // 可以跳转到视频详情页或其他操作 } else if (data.status === 'failed') { clearInterval(pollInterval); alert(`转码失败:${data.error || '未知错误'}`); } } catch (err) { console.error('获取进度失败:', err); } }, 1500); } // 调用示例:用户提交转码后,后端返回job_id,调用这个方法 // startProgressPolling('your-sidekiq-job-id-here');
三、额外优化建议
- Redis键管理:可以用Redis哈希表来存储单个任务的所有信息(进度、状态、错误),减少Redis键的数量,比如
hset "transcode_job:#{job_id}" progress 50 status running - 前端体验优化:可以给进度条添加动画效果,或者显示"转码中..."的提示
- 任务清理:可以定期清理Redis中过期的任务数据,或者在任务完成后主动删除相关键(不过设置过期时间已经足够)
- 权限控制:如果需要,在进度查询接口中验证用户是否有权限查看该任务的进度
内容的提问来源于stack exchange,提问作者dev404
相关产品推荐
相关产品推荐

