aboutsummaryrefslogtreecommitdiffstats
path: root/activestorage/app/models/active_storage/variant.rb
diff options
context:
space:
mode:
authorJanko Marohnić <janko.marohnic@gmail.com>2018-04-06 01:48:29 +0200
committerJanko Marohnić <janko.marohnic@gmail.com>2018-04-18 17:46:25 +0200
commitca1296858788780dcb5497e86f66b56170cca279 (patch)
tree1a54f8b10b9dd49c8c2c7581515d9efd6073fa7a /activestorage/app/models/active_storage/variant.rb
parent662ba236d115d3e2152b04dcdefdc0ee6f1f6102 (diff)
downloadrails-ca1296858788780dcb5497e86f66b56170cca279.tar.gz
rails-ca1296858788780dcb5497e86f66b56170cca279.tar.bz2
rails-ca1296858788780dcb5497e86f66b56170cca279.zip
Use ImageProcessing gem for ActiveStorage variants
ImageProcessing gem is a wrapper around MiniMagick and ruby-vips, and implements an interface for common image resizing and processing. This is the canonical image processing gem recommended in [Shrine], and that's where it developed from. The initial implementation was extracted from Refile, which also implements on-the-fly transformations. Some features that ImageProcessing gem adds on top of MiniMagick: * resizing macros - #resize_to_limit - #resize_to_fit - #resize_to_fill - #resize_and_pad * automatic orientation * automatic thumbnail sharpening * avoids the complex and inefficient MiniMagick::Image class * will use "magick" instead of "convert" on ImageMagick 7 However, the biggest feature of the ImageProcessing gem is that it has an alternative implementation that uses libvips. Libvips is an alternative to ImageMagick that can process images very rapidly (we've seen up 10x faster than ImageMagick). What's great is that the ImageProcessing gem provides the same interface for both implementations. The macros are named the same, and the libvips implementation does auto orientation and thumbnail sharpening as well; only the operations/options specific to ImageMagick/libvips differ. The integration provided by this PR should work for both implementations. The plan is to introduce the ImageProcessing backend in Rails 6.0 as the default backend and deprecate the MiniMagick backend, then in Rails 6.1 remove the MiniMagick backend.
Diffstat (limited to 'activestorage/app/models/active_storage/variant.rb')
-rw-r--r--activestorage/app/models/active_storage/variant.rb66
1 files changed, 32 insertions, 34 deletions
diff --git a/activestorage/app/models/active_storage/variant.rb b/activestorage/app/models/active_storage/variant.rb
index d84208419c..1cae2078f0 100644
--- a/activestorage/app/models/active_storage/variant.rb
+++ b/activestorage/app/models/active_storage/variant.rb
@@ -6,8 +6,18 @@ require "active_storage/downloading"
# These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the
# original.
#
-# Variants rely on {MiniMagick}[https://github.com/minimagick/minimagick] for the actual transformations
-# of the file, so you must add <tt>gem "mini_magick"</tt> to your Gemfile if you wish to use variants.
+# Variants rely on {ImageProcessing}[https://github.com/janko-m/image_processing] gem for the actual transformations
+# of the file, so you must add <tt>gem "image_processing"</tt> to your Gemfile if you wish to use variants. By
+# default, images will be processed with {ImageMagick}[http://imagemagick.org] using the
+# {MiniMagick}[https://github.com/minimagick/minimagick] gem, but you can also switch to the
+# {libvips}[http://jcupitt.github.io/libvips/] processor operated by the {ruby-vips}[https://github.com/jcupitt/ruby-vips]
+# gem).
+#
+# Rails.application.config.active_storage.processor
+# # => :mini_magick
+#
+# Rails.application.config.active_storage.processor = :vips
+# # => :vips
#
# Note that to create a variant it's necessary to download the entire blob file from the service and load it
# into memory. The larger the image, the more memory is used. Because of this process, you also want to be
@@ -18,7 +28,7 @@ require "active_storage/downloading"
# To refer to such a delayed on-demand variant, simply link to the variant through the resolved route provided
# by Active Storage like so:
#
-# <%= image_tag Current.user.avatar.variant(resize: "100x100") %>
+# <%= image_tag Current.user.avatar.variant(resize_to_fit: [100, 100]) %>
#
# This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController
# can then produce on-demand.
@@ -27,15 +37,22 @@ require "active_storage/downloading"
# has already been processed and uploaded to the service, and, if so, just return that. Otherwise it will perform
# the transformations, upload the variant to the service, and return itself again. Example:
#
-# avatar.variant(resize: "100x100").processed.service_url
+# avatar.variant(resize_to_fit: [100, 100]).processed.service_url
#
# This will create and process a variant of the avatar blob that's constrained to a height and width of 100.
# Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.
#
-# A list of all possible transformations is available at https://www.imagemagick.org/script/mogrify.php. You can
-# combine as many as you like freely:
+# Variant options are forwarded directly to the ImageProcessing gem. Visit the following links for a list of
+# available ImageProcessing commands and processor operations:
+#
+# * {ImageProcessing::MiniMagick}[https://github.com/janko-m/image_processing/blob/master/doc/minimagick.md#methods]
+# * {ImageMagick reference}[https://www.imagemagick.org/script/mogrify.php]
+# * {ImageProcessing::Vips}[https://github.com/janko-m/image_processing/blob/master/doc/vips.md#methods]
+# * {ruby-vips reference}[http://www.rubydoc.info/gems/ruby-vips/Vips/Image]
+#
+# You can combine as many of these options as you like freely:
#
-# avatar.variant(resize: "100x100", monochrome: true, flip: "-90")
+# avatar.variant(resize_to_fit: [100, 100], monochrome: true, flip: "-90")
class ActiveStorage::Variant
include ActiveStorage::Downloading
@@ -82,10 +99,10 @@ class ActiveStorage::Variant
end
def process
- open_image do |image|
- transform image
- format image
- upload image
+ download_blob_to_tempfile do |image|
+ variant = transform image
+ upload variant
+ variant.close!
end
end
@@ -102,31 +119,12 @@ class ActiveStorage::Variant
blob.content_type.presence_in(WEB_IMAGE_CONTENT_TYPES) || "image/png"
end
-
- def open_image(&block)
- image = download_image
-
- begin
- yield image
- ensure
- image.destroy!
- end
- end
-
- def download_image
- require "mini_magick"
- MiniMagick::Image.create(blob.filename.extension_with_delimiter) { |file| download_blob_to(file) }
- end
-
def transform(image)
- variation.transform(image)
- end
-
- def format(image)
- image.format("PNG") unless WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
+ format = "png" unless WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
+ variation.transform(image, format: format)
end
- def upload(image)
- File.open(image.path, "r") { |file| service.upload(key, file) }
+ def upload(file)
+ service.upload(key, file)
end
end