You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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,但轮询实现更简单,适合中小规模场景)。

步骤:

  1. 用户提交转码请求后,后端启动Sidekiq任务,并将job_id返回给前端
  2. 前端用这个job_id每隔1-2秒发送一次请求到后端的进度查询接口
  3. 后端接口从Redis中读取对应job_id的进度和状态,返回给前端
  4. 前端根据返回的数据更新页面上的进度条,当状态为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:20:34