Rails Active Storage多附件关联:非MP4视频转码回调实现需求
完整解决方案
针对你需要给Cloud模型添加视频转码回调的需求,我整理了一套可行的实现方案,分为模型修改、Sidekiq Worker编写和关键注意事项三部分:
1. 更新Cloud模型
我们使用after_commit回调来处理,因为Active Storage的附件会在记录提交后才完成保存,这样能确保我们拿到已持久化的视频附件。同时我们会过滤掉已处理过的视频,只针对新添加的非MP4格式视频触发转码任务:
class Cloud < ApplicationRecord has_many_attached :images has_many_attached :videos # 保留原有的验证规则 validates :images, content_type: { in: ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'], message: 'should be of type png, jpg, jpeg and gif.' } validates :videos, content_type: { in: ['video/mp4', 'video/x-flv', 'video/x-msvideo', 'video/x-ms-wmv', 'video/avi', 'video/quicktime'], message: 'should be of type mp4, x-flv, x-msvideo, x-ms-wmv, avi and quicktime(mov).' } validates :videos, size: { greater_than: 1.kilobytes, message: 'size is invalid' } validates :images, size: { greater_than: 1.kilobytes, message: 'size is invalid' } # 提交后触发转码任务,支持创建和更新场景 after_commit :queue_video_transcoding, on: [:create, :update] private def queue_video_transcoding # 遍历所有视频附件,筛选需要转码的目标 videos.each do |video| # 只处理新上传的、且不是MP4格式的视频 next unless video.previously_new_record? && video.content_type != 'video/mp4' # 将转码任务推入Sidekiq队列,传入Blob ID以便Worker定位视频 VideoTranscodingWorker.perform_async(video.blob.id) end end end
2. 创建视频转码Worker
接下来编写Sidekiq Worker来执行FFmpeg转码逻辑,Worker会负责下载原视频、转码为MP4、替换原附件的内容:
创建app/workers/video_transcoding_worker.rb文件:
class VideoTranscodingWorker include Sidekiq::Worker def perform(blob_id) blob = ActiveStorage::Blob.find(blob_id) # 下载原视频到临时文件 original_temp_file = blob.open # 创建转码后的临时MP4文件 transcoded_temp_file = Tempfile.new(["transcoded_#{blob.filename.base}", '.mp4'], binmode: true) begin # 执行FFmpeg转码命令,这里用了通用的H.264编码参数,你可以根据需求调整(比如分辨率、比特率) # 注意:必须确保服务器已经安装FFmpeg system( 'ffmpeg', '-i', original_temp_file.path, '-c:v', 'libx264', '-c:a', 'aac', '-strict', 'experimental', transcoded_temp_file.path, exception: true # 命令失败时抛出异常,让Sidekiq自动重试 ) # 替换原Blob的内容为转码后的MP4 blob.open do |file| file.binmode file.write(transcoded_temp_file.read) file.rewind end # 更新Blob的元数据,标记为MP4格式 blob.update!( content_type: 'video/mp4', filename: "#{blob.filename.base}.mp4" ) ensure # 无论转码成功或失败,都清理临时文件 original_temp_file.close! transcoded_temp_file.close! end end end
3. 关键注意事项
- FFmpeg安装:服务器必须安装FFmpeg才能执行转码。Ubuntu可以用
sudo apt-get install ffmpeg,macOS用brew install ffmpeg。 - 错误重试:Sidekiq会自动重试失败的任务,你可以在Worker里添加日志记录,方便排查转码失败的原因。
- 性能优化:视频转码是CPU密集型操作,建议调整Sidekiq的并发数,避免占用过多服务器资源;如果转码任务多,考虑用专门的转码服务器处理。
- 存储权限:确保Sidekiq进程有访问Active Storage存储服务(比如本地存储、S3等)的权限,否则会无法下载或上传文件。
内容的提问来源于stack exchange,提问作者Vishal G
相关产品推荐
相关产品推荐

