如何在Rails 5中使用Carrierwave生成各类文件的缩略图?
解决Heroku上Rails 5 Carrierwave生成多类型文件缩略图的问题
我来帮你搞定这个问题!你之前用convert命令在本地可能没问题,但Heroku环境缺了不少处理不同文件类型的依赖,下面是一步步的可行方案:
一、先配齐Heroku的环境依赖
Heroku默认没有处理视频、PDF、AI这些文件的工具,得先添加对应的Buildpacks(可以通过Heroku CLI或者网页控制台操作):
- 首先确保Ruby Buildpack是第一个(因为Heroku按顺序执行Buildpacks):
heroku buildpacks:add heroku/ruby --index 1 - 添加ImageMagick(处理图片、PDF、AI的基础工具):
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-imagemagick.git - 添加FFmpeg(处理视频缩略图):
heroku buildpacks:add https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git - 添加Ghostscript(增强ImageMagick处理PDF、AI的能力):
heroku buildpacks:add https://github.com/steeple-dev/heroku-buildpack-ghostscript.git - 如果要处理Excel(.xlsx),还要加LibreOffice Buildpack(用来把Excel转成PDF再生成缩略图):
heroku buildpacks:add https://github.com/socialtables/heroku-buildpack-libreoffice.git
二、自定义Carrierwave处理器处理多文件类型
Carrierwave默认只处理图片,所以我们写个自定义处理器,针对不同文件类型做不同处理:
1. 创建自定义处理器文件
在app/uploaders/下新建thumbnail_processor.rb:
class ThumbnailProcessor def initialize(file, options = {}) @file = file @output_path = options[:output_path] || "#{file.path}.thumb.png" end def process case file_extension when 'jpg', 'jpeg', 'png', 'gif' process_image when 'pdf' process_pdf when 'mp4', 'mov', 'avi' process_video when 'xlsx' process_xlsx when 'ai' process_ai end @output_path end private def file_extension @file.extension.downcase end # 处理图片类文件 def process_image system("convert -strip -thumbnail '100x100>' #{@file.path} #{@output_path}") end # 处理PDF(取第一页生成缩略图) def process_pdf system("convert -strip -thumbnail '100x100>' #{@file.path}[0] #{@output_path}") end # 处理视频(取第一帧生成缩略图) def process_video system("ffmpeg -i #{@file.path} -vframes 1 -vf 'scale=100:-1' -y #{@output_path}") end # 处理Excel:先转成PDF再生成缩略图 def process_xlsx temp_pdf = "#{File.dirname(@file.path)}/#{File.basename(@file.path, '.*')}.pdf" # 用LibreOffice转Excel为PDF system("libreoffice --headless --convert-to pdf #{@file.path} --outdir #{File.dirname(@file.path)}") # 基于PDF生成缩略图 system("convert -strip -thumbnail '100x100>' #{temp_pdf}[0] #{@output_path}") # 清理临时PDF文件 File.delete(temp_pdf) if File.exist?(temp_pdf) end # 处理AI文件(和PDF逻辑类似,取第一页) def process_ai system("convert -strip -thumbnail '100x100>' #{@file.path}[0] #{@output_path}") end end
2. 在Uploader中使用这个处理器
修改你的Carrierwave Uploader(比如app/uploaders/file_uploader.rb):
class FileUploader < CarrierWave::Uploader::Base # 用Fog存储(Heroku本地存储不能持久化,必须用云存储比如AWS S3) storage :fog # 生成缩略图版本 version :thumbnail do process :generate_thumbnail # 确保缩略图统一用PNG格式 def full_filename(for_file) "#{super.chomp(File.extname(super))}.png" end end private def generate_thumbnail processor = ThumbnailProcessor.new(file, output_path: current_path) processor.process end end
三、配置云存储(必须!)
Heroku的临时文件系统重启就会清空,所以一定要用云存储,比如AWS S3。在config/initializers/carrierwave.rb中配置Fog:
CarrierWave.configure do |config| config.fog_provider = 'fog/aws' config.fog_credentials = { provider: 'AWS', aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'], aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], region: ENV['AWS_REGION'] } config.fog_directory = ENV['AWS_S3_BUCKET'] # 开发环境可以用本地存储,生产环境用S3 config.storage = Rails.env.production? ? :fog : :file end
然后在Heroku设置环境变量:
heroku config:set AWS_ACCESS_KEY_ID=你的AWS密钥ID AWS_SECRET_ACCESS_KEY=你的AWS密钥 AWS_REGION=你的S3区域 AWS_S3_BUCKET=你的S3桶名
四、额外注意事项
- 错误处理:可以在处理器里加判断,比如检查
system命令的返回值,如果失败就生成一张默认缩略图(比如提前上传一张"no-preview.png"到S3,然后返回它的路径)。 - 文件大小限制:如果处理大文件,要注意Heroku的超时限制,必要的话可以用后台任务(比如Sidekiq)来生成缩略图,避免请求超时。
- 测试:在本地先模拟Heroku环境测试,或者直接在Heroku上用
heroku run rails console调试处理器的执行情况。
内容的提问来源于stack exchange,提问作者Subha
相关产品推荐
相关产品推荐

