Reputation: 95
I want to convert user uploading images in webp
for site performance. I know webp
now support only on two browsers, Google Chrome
and Opera
.
I am using carrierwave
for image uploading to s3
.
Not find any support of converting image to webp
in carrierwave
.
image-magick
have libraries libwebp
which can convert images to webp
.
Is there any gem of that in rails?
How I can convert image to carrierwave
and save it also.
Solution other than carrierwave
also works.
Upvotes: 5
Views: 6640
Reputation: 5
module WebPConverter
private
def build_webp_full_filename(filename, version_name)
return "#{version_name}_#{filename}" if filename.split('.').last == 'webp'
"#{version_name}_#{filename}.webp"
end
end
and in ImageUploader
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
include CarrierWave::WebP::Converter
include WebPConverter
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process resize_to_fit: [50, 50]
end
version :img_quality do
process resize_to_fit: [600, 600]
process quality: 60
end
version :webp do
process convert_to_webp: [{ quality: 60, method: 5 }]
process resize_to_fit: [600, 600]
def full_filename(file)
build_webp_full_filename(file, version_name)
end
end
def extension_whitelist
%w[jpg jpeg gif png]
end
end
and providing different formats depending on the browser gem 'browser'
in views:
<%= image_tag set_browser(deals_first(@category).brand.logo) %>
helper
def set_browser(object)
browser.chrome? || browser.opera? || browser.platform.android? ?
object.webp.url : object.quality.url
end
Upvotes: 0
Reputation: 118
I had similar task but without uploading to S3. I used webp-ffi gem to write custom converter.
This is my solution that helped me.
First you need to install requirements specified in README file of webp-ffi gem.
After that add webp-ffi gem to your project:
gem 'webp-ffi'
If you don't need to store original images you can just add custom method to your uploader and call it with process method of CarrierWave. Here is my hack for converting images to webp format:
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
You can move this method to some module located in your project (make sure it will autoload correctly). For example I put this code in 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
Now I can include this module in every uploader that needs webp conversion:
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
But if you need to store original version of file and create a version inside uploader you will need to use this 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
This has to be done because CarrierWave uses original image name to build paths to other versions.
Also you can move logic from #full_filename into a method. For example I moved the logic of building full filename into WebPConverter module so it look something like this:
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
And from now I can use it for my versions that needs conversion to 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
Checkout carrierwave-webp gem that I used as example to create my solution to this problem (this gem did not worked for me for some reason).
Also checkout my simple app that I made to demonstrate working solution.
Upvotes: 8