ruby-on-rails - Shrine Gem Ruby on Rails 无服务器图像处理程序,后台作业
问题描述
我正在使用 Shrine Gem 使用 S3 分段上传来处理我的 Ruby on Rails 项目中的文件。我想使用 Derivatives 插件指向一个 AWS Lamdba 无服务器图像处理程序作为我的衍生 remote_url,并在后台作业中处理这些衍生。我刚刚完成了这项工作,但有一个问题:
上传文件后,该记录立即与 Shrinecache
存储 url 一起保存。后台作业完成处理并创建派生后,该promote
作业将attachment_url
使用store
端点更新。
所以,我想要做的是store
首先将原始 URL 提升到存储桶(这样default_url
将指向store
存储桶,而不是缓存),然后再处理衍生品。但是,我无法完全弄清楚如何做到这一点。我的 Shrine 初始化程序和上传程序如下。
# config/initializers/shrine.rb
require 'shrine'
require 'shrine/storage/s3'
# require "shrine/storage/file_system"
s3_options = Rails.application.credentials.s3
Shrine.storages = {
cache: Shrine::Storage::S3.new(prefix: 'cache', public: true, **s3_options),
store: Shrine::Storage::S3.new(prefix: 'media', public: true, **s3_options),
}
Shrine.plugin :activerecord # Load Active Record integration
Shrine.plugin :cached_attachment_data # For retaining cached file on form redisplays
Shrine.plugin :determine_mime_type
Shrine.plugin :infer_extension
Shrine.plugin :instrumentation
Shrine.plugin :pretty_location
Shrine.plugin :remote_url, max_size: 40*1024*1024 # ~40mb
Shrine.plugin :restore_cached_data # Refresh metadata for cached files
Shrine.plugin :type_predicates
Shrine.plugin :uppy_s3_multipart # Enable S3 multipart upload for Uppy https://github.com/janko/uppy-s3_multipart
Shrine.plugin :url_options, store: { host: Rails.application.credentials.asset_host } if Rails.application.credentials.asset_host.present?
# app/uploaders/file_uploader.rb
class FileUploader < Shrine
plugin :derivatives
plugin :default_url
plugin :backgrounding
# TODO: images returned by Shrine.remote_url have file extension set as .jpeg, not .jpg, which is annoying
# TODO: set up URL fallbacks for backgrounded derivatives? https://shrinerb.com/docs/processing#url-fallbacks
# The Cloudfront URL generated by the serverless image handler
SERVERLESS_IMAGE_HOST = Rails.application.credentials.image_handler_host
DEFAULT_EDITS = {
rotate: 'auto',
quality: 60,
progressive: true,
chromaSubsampling: '4:4:4',
withoutEnlargement: true,
sharpen: true
}
# Fall back to the original file URL when the derivative
# https://shrinerb.com/docs/processing#url-fallbacks
Attacher.default_url do |derivative: nil, **|
file&.url if derivative
end
# Perform derivative transformations inside a background job
Attacher.promote_block do |**options|
if file.image?
# TODO: the initially promoted file URL is saved to the record as the cache URL. We need to
# promote the original image first, then perform the derivative job.
# promote
AttachmentDerivativeJob.perform_later(self.class.name, record.class.name, record.id, name, file_data)
else
promote
end
end
# The derivatives plugin allows storing processed files ("derivatives") alongside the main attached file
# https://shrinerb.com/docs/plugins/derivatives
Attacher.derivatives do |original|
def serverless_image_request(edits = {})
request_path = Base64.strict_encode64({
bucket: Shrine.storages[:cache].bucket.name,
key: [Shrine.storages[:cache].prefix, record.attachment.id].reject(&:blank?).join('/'), # The aws object key of the original image in the `store` S3 bucket
edits: edits
}.to_json).chomp
"#{SERVERLESS_IMAGE_HOST}/#{request_path}"
end
if file.image?
process_derivatives(:image, original)
else
process_derivatives(:file, original)
end
end
Attacher.derivatives :image do |original|
{
thumb: Shrine.remote_url( serverless_image_request({
resize: {
width: 200,
height: 200,
fit: 'cover'
}.reverse_merge(DEFAULT_EDITS)
})),
small: Shrine.remote_url( serverless_image_request({
resize: {
width: 600,
height: 600,
fit: 'inside'
}.reverse_merge(DEFAULT_EDITS)
})),
medium: Shrine.remote_url( serverless_image_request({
resize: {
width: 1200,
height: 1200,
fit: 'inside'
}.reverse_merge(DEFAULT_EDITS)
})),
large: Shrine.remote_url( serverless_image_request({
resize: {
width: 2200,
height: 2200,
fit: 'inside'
}.reverse_merge(DEFAULT_EDITS)
}))
}
end
Attacher.derivatives :file do |original|
{}
end
end
解决方案
activerecord_after_save
更新 - 我能够通过覆盖Attacher 类中的方法来配置我的 Uploader 类,使其执行我想要的方式。不确定这是否是最好的解决方案,但它似乎符合我的预期。
现在,当我上传一系列图像时,它们会直接上传到 S3 并且原始文件会被提升为永久 Shrine store
。在保存后回调中,我触发了AttachmentDerivativeJob
,它使用 AWS Lambda 的默认无服务器图像处理程序处理衍生品。
class FileUploader < Shrine
plugin :derivatives
plugin :default_url
# The Cloudfront URL generated by the serverless image handler
SERVERLESS_IMAGE_HOST = Rails.application.credentials.image_handler_host
DEFAULT_EDITS = {
rotate: 'auto',
quality: 60,
progressive: true,
chromaSubsampling: '4:4:4',
withoutEnlargement: true,
sharpen: true
}
# Active Record - Overriding callbacks
# https://shrinerb.com/docs/plugins/activerecord#overriding-callbacks
class Attacher
private
def activerecord_after_save
super
if file.image? && derivatives.blank?
AttachmentDerivativeJob.perform_later(self.class.name, self.record.class.name, self.record.id, self.name, self.file_data)
end
end
end
# Fall back to the original file URL when the derivative
# https://shrinerb.com/docs/processing#url-fallbacks
Attacher.default_url do |derivative: nil, **|
file&.url if derivative
end
# The derivatives plugin allows storing processed files ("derivatives") alongside the main attached file
# https://shrinerb.com/docs/plugins/derivatives
Attacher.derivatives do |original|
def serverless_image_request(edits = {})
request_path = Base64.strict_encode64({
bucket: Shrine.storages[:store].bucket.name,
key: [Shrine.storages[:store].prefix, record.attachment.id].reject(&:blank?).join('/'), # The aws object key of the original image in the `store` S3 bucket
edits: edits
}.to_json).chomp
"#{SERVERLESS_IMAGE_HOST}/#{request_path}"
end
if file.image?
process_derivatives(:image, original)
else
process_derivatives(:file, original)
end
end
Attacher.derivatives :image do |original|
{
thumb: Shrine.remote_url( serverless_image_request({
resize: {
width: 200,
height: 200,
fit: 'cover'
}.reverse_merge(DEFAULT_EDITS)
})),
small: Shrine.remote_url( serverless_image_request({
resize: {
width: 600,
height: 600,
fit: 'inside'
}.reverse_merge(DEFAULT_EDITS)
})),
medium: Shrine.remote_url( serverless_image_request({
resize: {
width: 1200,
height: 1200,
fit: 'inside'
}.reverse_merge(DEFAULT_EDITS)
})),
large: Shrine.remote_url( serverless_image_request({
resize: {
width: 2200,
height: 2200,
fit: 'inside'
}.reverse_merge(DEFAULT_EDITS)
}))
}
end
Attacher.derivatives :file do |original|
{}
end
end
推荐阅读
- flutter - 如何在字符串中使用列表 - 颤振
- c# - 无法从 WPF 数据网格更新 SQL
- python - Ubuntu Groovy Gorilla 中 Geany 的问题
- node.js - Puppeteer [错误:执行上下文被破坏,很可能是因为导航。]
- c++ - 在 ubuntu mingw 上指定线程模型(如果可能)
- angular - 单击按钮时组件不会重新加载
- python - 如何从 json 元素快速创建二维 numpy 数组?
- r - R markdown:将数据框中的值报告为文本
- python - Ubuntu 20.04 中的语音识别模块 [python] 出现问题
- xpath - 如何获取特定的 xpath 标记值