Shrine Gem Ruby on Rails 无服务器图像处理程序,后台作业

Posted

技术标签:

【中文标题】Shrine Gem Ruby on Rails 无服务器图像处理程序,后台作业【英文标题】:Shrine Gem Ruby on Rails Serverless Image Handler, Background Job 【发布时间】:2020-11-02 00:56:15 【问题描述】:

我正在使用 Shrine Gem 通过 S3 分段上传来处理我的 Ruby on Rails 项目中的文件。我想使用 Derivatives 插件指向一个 AWS Lamdba 无服务器图像处理程序作为我的衍生 remote_url,并在后台作业中处理这些衍生。我刚刚完成了这项工作,但有一个问题:

在上传文件后,该记录将与 Shrine cache 存储 url 一起保存。后台作业完成处理并创建派生后,promote 作业将 attachment_url 更新为 store 端点。

所以,我想要做的是首先将原始 URL 提升到 store 存储桶(这样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

【问题讨论】:

【参考方案1】:

更新 - 通过覆盖 Attacher 类中的 activerecord_after_save 方法,我能够配置我的 Uploader 类,使其执行我想要的方式。不确定这是否是最佳解决方案,但它的行为似乎符合我的预期。

现在,当我上传一系列图像时,它们会直接上传到 S3,原始文件会升级为永久 Shrine store。在保存后回调中,我触发了AttachmentDerivativeJob,它使用AWS Lambda 的默认serverless image handler 处理衍生品。

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

【讨论】:

以上是关于Shrine Gem Ruby on Rails 无服务器图像处理程序,后台作业的主要内容,如果未能解决你的问题,请参考以下文章

ruby on rails升级资产预编译失败

Ruby on Rails 的社交媒体共享按钮 Gem

是否有 Ruby on Rails 的分析 gem/插件?

ruby 使用Devise gem验证您的API,使用标头标记。 Ruby on Rails 4.阅读评论。

paypal gem 多个帐户 ruby​​ on rails

如何在 ruby​​ on rails 中使用 apns gem 发送推送通知 (rpush)