rails/activestorage/app/models/active_storage/variant.rb

119 lines
5.2 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
# = Active Storage \Variant
#
2017-07-21 21:34:28 +00:00
# Image blobs can have variants that are the result of a set of transformations applied to the original.
2017-07-24 17:05:15 +00:00
# These variants are used to create thumbnails, fixed-size avatars, or any other derivative image from the
# original.
#
2021-12-29 21:22:18 +00:00
# Variants rely on {ImageProcessing}[https://github.com/janko/image_processing] gem for the actual transformations
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.
2018-04-05 23:48:29 +00:00
# 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://libvips.github.io/libvips/] processor operated by the {ruby-vips}[https://github.com/libvips/ruby-vips]
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.
2018-04-05 23:48:29 +00:00
# gem).
#
# Rails.application.config.active_storage.variant_processor
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.
2018-04-05 23:48:29 +00:00
# # => :mini_magick
#
# Rails.application.config.active_storage.variant_processor = :vips
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.
2018-04-05 23:48:29 +00:00
# # => :vips
2017-07-24 17:05:15 +00:00
#
# Note that to create a variant it's necessary to download the entire blob file from the service. Because of this process,
# you also want to be considerate about when the variant is actually processed. You shouldn't be processing variants inline
# in a template, for example. Delay the processing to an on-demand controller, like the one provided in
# ActiveStorage::RepresentationsController.
2017-07-24 17:05:15 +00:00
#
# 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_to_limit: [100, 100]) %>
2017-07-24 17:05:15 +00:00
#
# This will create a URL for that specific blob with that specific variant, which the ActiveStorage::RepresentationsController
2017-07-24 17:05:15 +00:00
# can then produce on-demand.
#
# When you do want to actually produce the variant needed, call +processed+. This will check that the variant
2017-07-24 17:05:15 +00:00
# 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_to_limit: [100, 100]).processed.url
2017-07-24 17:05:15 +00:00
#
# 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.
#
# You can combine any number of ImageMagick/libvips operations into a variant, as well as any macros provided by the
# ImageProcessing gem (such as +resize_to_limit+):
#
# avatar.variant(resize_to_limit: [800, 800], colourspace: "b-w", rotate: "-90")
#
# Visit the following links for a list of available ImageProcessing commands and ImageMagick/libvips operations:
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.
2018-04-05 23:48:29 +00:00
#
2021-12-29 21:22:18 +00:00
# * {ImageProcessing::MiniMagick}[https://github.com/janko/image_processing/blob/master/doc/minimagick.md#methods]
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.
2018-04-05 23:48:29 +00:00
# * {ImageMagick reference}[https://www.imagemagick.org/script/mogrify.php]
2021-12-29 21:22:18 +00:00
# * {ImageProcessing::Vips}[https://github.com/janko/image_processing/blob/master/doc/vips.md#methods]
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.
2018-04-05 23:48:29 +00:00
# * {ruby-vips reference}[http://www.rubydoc.info/gems/ruby-vips/Vips/Image]
2017-07-11 16:53:17 +00:00
class ActiveStorage::Variant
include ActiveStorage::Blob::Servable
2017-07-11 16:53:17 +00:00
attr_reader :blob, :variation
delegate :service, to: :blob
delegate :content_type, to: :variation
2017-07-11 16:53:17 +00:00
2017-09-28 20:43:37 +00:00
def initialize(blob, variation_or_variation_key)
@blob, @variation = blob, ActiveStorage::Variation.wrap(variation_or_variation_key)
2017-07-11 16:53:17 +00:00
end
2017-07-24 17:05:15 +00:00
# Returns the variant instance itself after it's been processed or an existing processing has been found on the service.
2017-07-20 22:35:15 +00:00
def processed
2017-07-23 21:28:45 +00:00
process unless processed?
2017-07-20 22:35:15 +00:00
self
end
2017-07-24 17:05:15 +00:00
# Returns a combination key of the blob and the variation that together identifies a specific variant.
2017-07-21 20:51:31 +00:00
def key
"variants/#{blob.key}/#{OpenSSL::Digest::SHA256.hexdigest(variation.key)}"
2017-07-11 16:53:17 +00:00
end
# Returns the URL of the blob variant on the service. See {ActiveStorage::Blob#url} for details.
2017-07-24 17:05:15 +00:00
#
# Use <tt>url_for(variant)</tt> (or the implied form, like <tt>link_to variant</tt> or <tt>redirect_to variant</tt>) to get the stable URL
# for a variant that points to the ActiveStorage::RepresentationsController, which in turn will use this +service_call+ method
2017-07-24 17:05:15 +00:00
# for its redirection.
def url(expires_in: ActiveStorage.service_urls_expire_in, disposition: :inline)
service.url key, expires_in: expires_in, disposition: disposition, filename: filename, content_type: content_type
2017-07-11 16:53:17 +00:00
end
# Downloads the file associated with this variant. If no block is given, the entire file is read into memory and returned.
# That'll use a lot of RAM for very large files. If a block is given, then the download is streamed and yielded in chunks.
def download(&block)
service.download key, &block
end
def filename
ActiveStorage::Filename.new "#{blob.filename.base}.#{variation.format.downcase}"
end
2017-10-13 11:52:39 +00:00
# Returns the receiving variant. Allows ActiveStorage::Variant and ActiveStorage::Preview instances to be used interchangeably.
def image
self
end
# Deletes variant file from service.
def destroy
service.delete(key)
end
2017-07-11 16:53:17 +00:00
private
2017-07-23 21:28:45 +00:00
def processed?
service.exist?(key)
end
2017-07-20 22:35:15 +00:00
def process
2019-05-15 19:53:43 +00:00
blob.open do |input|
variation.transform(input) do |output|
2019-09-17 04:08:22 +00:00
service.upload(key, output, content_type: content_type)
2019-05-15 19:53:43 +00:00
end
end
end
2017-07-11 16:53:17 +00:00
end