如何在 ruby​​ on rails 中将用户上传的图像转换为 webp

Waq*_*had 3 ruby-on-rails imagemagick image-processing carrierwave webp

我想将用户上传的图像转换webp为网站性能。我知道webp现在仅支持两种浏览器,Google Chrome并且Opera.

我正在使用carrierwave图像上传到s3.

找不到任何将图像转换为webpin 的支持carrierwave

image-magicklibwebp可以将图像转换为webp.

Rails 中有这样的宝石吗?

我如何将图像转换为carrierwave并保存它。除此以外的解决方案carrierwave也有效。

psh*_*hnn 5

我有类似的任务,但没有上传到 S3。我使用webp-ffi gem 编写自定义转换器。

这是我的解决方案,对我有帮助。

首先,您需要安装webp-ffi gem 的README 文件中指定的要求

之后将webp-ffi gem添加到您的项目中:

gem 'webp-ffi'
Run Code Online (Sandbox Code Playgroud)

如果您不需要存储原始图像,您可以向上传器添加自定义方法并使用 CarrierWave 的 process 方法调用它。这是我将图像转换为 webp 格式的技巧:

class MyImageUploader < CarrierWave::Uploader::Base
  storage :file

  process convert_to_webp: [{ quality: 80, method: 5 }]

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  private

  def convert_to_webp(options = {})
    # Build path for new file
    webp_path = "#{path}.webp"

    # Encode (convert) image to webp format with passed options
    WebP.encode(path, webp_path, options)

    # HACK: Changing of this two instance variables is the only way 
    #   I found to make CarrierWave save new file that was created 
    #   by encoding original image.
    @filename = webp_path.split('/').pop

    @file = CarrierWave::SanitizedFile.new(
      tempfile: webp_path,
      filename: webp_path,
      content_type: 'image/webp'
    )
  end
end
Run Code Online (Sandbox Code Playgroud)

您可以将此方法移动到位于项目中的某个模块(确保它会正确自动加载)。例如我把这段代码放在app/services/web_p_converter.rb

module WebPConverter
  def convert_to_webp(options = {})
    webp_path = "#{path}.webp"

    WebP.encode(path, webp_path, options)

    @filename = webp_path.split('/').pop

    @file = CarrierWave::SanitizedFile.new(
      tempfile: webp_path,
      filename: webp_path,
      content_type: 'image/webp'
    )
  end
end
Run Code Online (Sandbox Code Playgroud)

现在我可以在每个需要 webp 转换的上传器中包含这个模块:

class MyImageUploader < CarrierWave::Uploader::Base
  include WebPConverter

  storage :file

  process convert_to_webp: [{ quality: 80, method: 5 }]

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end
Run Code Online (Sandbox Code Playgroud)

但是如果你需要存储文件的原始版本并在上传器中创建一个版本,你将需要使用这个 hack:

class MyImageUploader < CarrierWave::Uploader::Base
  include WebPConverter

  storage :file

  version :webp do
    process convert_to_webp: [{ quality: 80, method: 5 }]

    def full_filename(file)
      return "#{version_name}_#{filename}" if filename.split('.').last == 'webp'

      "#{version_name}_#{file}.webp"
    end
  end

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end
Run Code Online (Sandbox Code Playgroud)

必须这样做,因为 CarrierWave 使用原始图像名称来构建其他版本的路径。

您也可以将逻辑从 #full_filename 移动到方法中。例如,我将构建完整文件名的逻辑移动到 WebPConverter 模块中,因此它看起来像这样:

module WebPConverter
  # ...

  def build_webp_full_filename(filename, version_name)
    return "#{version_name}_#{filename}" if filename.split('.').last == 'webp'

    "#{version_name}_#{filename}.webp"
  end
end
Run Code Online (Sandbox Code Playgroud)

从现在开始,我可以将它用于需要转换为 webp 的版本:

class MyImageUploader < CarrierWave::Uploader::Base
  include WebPConverter

  storage :file

  version :webp do
    process convert_to_webp: [{ quality: 80, method: 5 }]

    def full_filename(file)
      build_webp_full_filename(file, version_name)
    end
  end

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end
Run Code Online (Sandbox Code Playgroud)

签出我用作示例的carrierwave-webp gem 来创建我的解决方案(由于某种原因,这个gem 对我不起作用)。

还可以查看我为演示工作解决方案而制作的简单应用程序