Reputation: 41
So after reading the official documentations, I'm using the Active Storage variant this way
<%= image_tag user.avatar.variant(resize: "100x100") %>
The problem is that when I look at my S3 storage I notice that I have the original file (for example a size 22kb) and the variant file which contains a 2kb file (the resized one).
I know in the documentation says:
.."When the browser hits the variant URL, Active Storage will lazily transform the original blob into the specified format and redirect to its new service location."
But is there a way to prevent Active Storage to generate an additional variant file? Like making the variant one, the original. So instead of 24kb of resurces in the S3 I have only 2kb.
Much thanks!
EDIT:
I manage to solve the issue applying some answers, If anyone comes accross this:
def resize_avatar2
avatar_f = avatar.last
if avatar_f.nil?
return
end
resized_image = MiniMagick::Image.read(avatar_f.download)
resized_image = resize_with_crop(resized_image, 300, 300)
v_filename = avatar_f.filename
v_content_type = avatar_f.content_type
avatar.all.each { |imagen| imagen.purge }
avatar.attach(
io: File.open(resized_image.path),
filename: v_filename,
content_type: v_content_type)
end
Create a helper, include it with this methods:
def resize_with_nocrop(image, w, h)
w_original = image[:width].to_f
h_original = image[:height].to_f
if (w_original*h != h_original * w)
if w_original*h >= h_original * w
# long width
w_result = w
h_result = w_result * (h_original / w_original)
elsif w_original*h <= h_original * w
# long height
h_result = h
w_result = h_result * (w_original / h_original)
end
else
# good proportions
h_result = h
w_result = w
end
#
image.resize("#{w_result}x#{h_result}")
return image
end
def resize_with_crop(img, w, h, options = {})
gravity = options[:gravity] || :center
w_original, h_original = [img[:width].to_f, img[:height].to_f]
op_resize = ''
# check proportions
if w_original * h < h_original * w
op_resize = "#{w.to_i}x"
w_result = w
h_result = (h_original * w / w_original)
else
op_resize = "x#{h.to_i}"
w_result = (w_original * h / h_original)
h_result = h
end
w_offset, h_offset = crop_offsets_by_gravity(gravity, [w_result, h_result], [ w, h])
img.combine_options do |i|
i.resize(op_resize)
i.gravity(gravity)
i.crop "#{w.to_i}x#{h.to_i}+#{w_offset}+#{h_offset}!"
end
img
end
GRAVITY_TYPES = [ :north_west, :north, :north_east, :east, :south_east, :south, :south_west, :west, :center ]
def crop_offsets_by_gravity(gravity, original_dimensions, cropped_dimensions)
raise(ArgumentError, "Gravity must be one of #{GRAVITY_TYPES.inspect}") unless GRAVITY_TYPES.include?(gravity.to_sym)
raise(ArgumentError, "Original dimensions must be supplied as a [ width, height ] array") unless original_dimensions.kind_of?(Enumerable) && original_dimensions.size == 2
raise(ArgumentError, "Cropped dimensions must be supplied as a [ width, height ] array") unless cropped_dimensions.kind_of?(Enumerable) && cropped_dimensions.size == 2
original_width, original_height = original_dimensions
cropped_width, cropped_height = cropped_dimensions
vertical_offset = case gravity
when :north_west, :north, :north_east then 0
when :center, :east, :west then [ ((original_height - cropped_height) / 2.0).to_i, 0 ].min
when :south_west, :south, :south_east then (original_height - cropped_height).to_i
end
horizontal_offset = case gravity
when :north_west, :west, :south_west then 0
when :center, :north, :south then [ ((original_width - cropped_width) / 2.0).to_i, 0 ].min
when :north_east, :east, :south_east then (original_width - cropped_width).to_i
end
return [ horizontal_offset, vertical_offset ]
end
Upvotes: 1
Views: 3158
Reputation: 1895
If you want to replace the originals with the variants, I think the best way is to resize the image before it's uploaded.
e.g. Using params[:user][:avatar]
to find the path of the temp file, run ffmpeg on it (there are gems for it) and then upload the result.
Alternatively you could do processing on the image in job after it's uploaded:
User
), add an after_create :resize_image
resize_image
method with ImagesJob.resize(self).perform_later
resize(user)
method), you would:
url = user.avatar.variant(resize: "100x100").url
(Rails's generated URL, not the S3 one)image = open(url).read
(require 'open-uri'
needed)ActiveStorage::Blob
(active_storage_blobs
table) for that.image
as the new image user.avatar.attach(StringIO.new(image)); user.save
(Not tested but probably quite close)
Upvotes: 2