ActiveStorage Thumbnail Persistence - ruby-on-rails

I have migrated my Rails app to 5.2.0. Before I was using Paperclip. Paperclip generates different variants like thumbnail and avatar when an image is uploaded. How can I achieve this with ActiveStorage? I know we can do this user.avatar.variant(resize_to_fit: [100, 100]) but to me it's like doing this over and over again. I'm aiming to do pre-processing of these variants once it's uploaded.
Also you guys can suggest a better technique if this is bad from your experience.

Using .processed is the correct way to check if that variant was already processed and uploaded to the storage service.
One thing that Paperclip did nicely was the styles: {} object, in which you could list all the different transformations you wanted to do for thumbnails, etc, and name them.
Here's how I am handling named & stored transformations. This also keeps my template syntax shorter:
class Image < ActiveRecord::Base
has_one_attached :image_file
def self.sizes
{
thumbnail: { resize: "100x100" },
hero1: { resize: "1000x500" }
}
end
def sized(size)
self.image_file.variant(Image.sizes[size]).processed
end
end
Then in a template, say I have #image, I can simply call #image.sized(:hero1)

#aguardientico is correct that by add the .processed method to your variant object which will use the blob key to check if the file already exists on your service before attempting to re-produce the whole process again.
Also something to know in addition is the resize_to_fit is a ImageProcessing gem transformation method and is not supported yet by Rails 5.2. Instead right now it uses MiniMagick where you would append > to the resize method for paperclip.
so rewritten it would look like user.avatar.variant(resize: "100x100>")

Related

Rails 6 + Active Storage : set a default image that can have a variant

I need to set a default image for active storage attachments, in case of no image was uploaded.
I could make a simple helper calling an image from /assets/images but in this case, those images won't work with variants (e.g. <%= image_tag image.variant(resize_to_fill: [50,50]) %>)
Browsing the web, I found this interesting article that gives some clues : https://gorails.com/forum/how-do-i-set-and-use-a-default-image-with-active-storage, but it is not enough for my case.
Attaching the image with a before_create callback sounds overkill to me, as it is going to upload the same image several times to the storage.
Is there a way to set a default picture that is going to behave like any Active Storage attachment, and accepts variants ?
Is there a way to make minimagick variants working with "normal" images from assets path ?
Create a Method to fetch image . Follow the below snippet:
def fetch_cover_image
if self.cover_image.attached?
Rails.application.routes.url_helpers.rails_blob_url(self.cover_image,
host: Rails.application.credentials[Rails.env.to_sym][:host])
else
ActionController::Base.helpers.image_url('cover_image.jpg')
end
end

Images Rotating on Upload in Rails 5.2 Using AWS S3 and Activerecord [duplicate]

On Rails 5.2 I am trying to save an avatar via ActiveStorage but it seems as though not image oriantation data is being saved in the active storage blob.
I am saving the avatar via a file_field on a create action my
#user model
has_one_attached :avatar
private
def avatar_validation
if avatar.attached?
if avatar.blob.byte_size > 1000000
avatar.purge
errors.add(:avatar, 'file is too large')
elsif !avatar.blob.content_type.in?(%w[image/png image/jpg
image/jpeg])
avatar.purge
errors.add(:avatar, 'file type needs to be JPEG, JPG, or PNG')
end
end
end
I have been reading some documentation for minimagick https://github.com/minimagick/minimagick but have not figured out how I can associate
user.avatar.blob
with
image = MiniMagick::Image.open("input.jpg")
I have tried
image = MiniMagick::Image.open("user.avatar.blob")
but have had no luck
I need to try and figure this out because some avatars stored in active storage are being displayed rotated 90 degrees.
https://edgeguides.rubyonrails.org/active_storage_overview.html
talks of image processing but I have also had no luck with the gem rails recommends
I think you want to use a variant when displaying the image rather than trying to edit the stored image. To fix the orientation, you could say:
user.avatar.variant(auto_orient: true)
And if you want to do several operations at once (rather than in a pipeline), use combine_options:
user.avatar.variant(combine_options: {
auto_orient: true,
gravity: 'center',
resize: '23x42', # Using real dimensions of course.
crop: '23x42+0+0'
})
The edited image will be cached so you only do the transformation work on first access. You might want to put your variants into view helpers (or maybe even a model concern depending on your needs) so that you can isolate the noise.
You might want to refer to the API docs as well as the guide:
ActiveStorage::Variant
ActiveStorage::Variation

Rails 5.2 ActiveStorage save and then read Exif data

On Rails 5.2 I am trying to save an avatar via ActiveStorage but it seems as though not image oriantation data is being saved in the active storage blob.
I am saving the avatar via a file_field on a create action my
#user model
has_one_attached :avatar
private
def avatar_validation
if avatar.attached?
if avatar.blob.byte_size > 1000000
avatar.purge
errors.add(:avatar, 'file is too large')
elsif !avatar.blob.content_type.in?(%w[image/png image/jpg
image/jpeg])
avatar.purge
errors.add(:avatar, 'file type needs to be JPEG, JPG, or PNG')
end
end
end
I have been reading some documentation for minimagick https://github.com/minimagick/minimagick but have not figured out how I can associate
user.avatar.blob
with
image = MiniMagick::Image.open("input.jpg")
I have tried
image = MiniMagick::Image.open("user.avatar.blob")
but have had no luck
I need to try and figure this out because some avatars stored in active storage are being displayed rotated 90 degrees.
https://edgeguides.rubyonrails.org/active_storage_overview.html
talks of image processing but I have also had no luck with the gem rails recommends
I think you want to use a variant when displaying the image rather than trying to edit the stored image. To fix the orientation, you could say:
user.avatar.variant(auto_orient: true)
And if you want to do several operations at once (rather than in a pipeline), use combine_options:
user.avatar.variant(combine_options: {
auto_orient: true,
gravity: 'center',
resize: '23x42', # Using real dimensions of course.
crop: '23x42+0+0'
})
The edited image will be cached so you only do the transformation work on first access. You might want to put your variants into view helpers (or maybe even a model concern depending on your needs) so that you can isolate the noise.
You might want to refer to the API docs as well as the guide:
ActiveStorage::Variant
ActiveStorage::Variation

ROR: Paperclip styles

I have a model that handles all my uploads of different filetypes.
How do I create a style with the same name as the :basename so that the url will be the same for images and non-image files?
Try this
class Upload < ActiveRecord::Base
has_attached_file :photo,
:styles => {
:thumb => {"115x70>"},
:orig => {"300x168>"} }
..
As long as you specify two different styles, it'll create two different styles associated with your Upload object.
Then you can call them via :
= image_tag #upload.photo.url(:thumb)
= image_tag #upload.photo.url(:orig)
You are going to need to create a custom processor, then inside that processor you can call the IM methods for images and ignore the rest.
I didn't put much research into it, but this link might get you headed in the right direction: http://thewebfellas.com/blog/2009/2/22/video-thumbnails-with-ffmpeg-and-paperclip
Huh?
http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/ClassMethods#has_attached_file-instance_method
The thumbnails will be created when
the new file is assigned, but they
will not be saved until save is called
on the record. Likewise, if the
attribute is set to nil is called on
it, the attachment will not be deleted
until save is called. See the
Paperclip::Attachment documentation
for more specifics.
I know this is a simple question, but are you sure you have ImageMagick installed properly? Most problems that I've ran in to happen because ImageMagick isn't compiled/installed correctly. If you watch the logs, Paperclip will hum along and silently fail.

Rails: Preventing Duplicate Photo Uploads with Paperclip?

Is there anyway to throw a validation error if a user tries to upload the same photo twice to a Rails app using Paperclip? Paperclip doesn't seem to offer this functionality...
I'm using Rails 2.3.5 and Paperclip (obviously).
SOLUTION: (or one of them, at least)
Using Beerlington's suggestion, I decided to go with an MD5 Checksum comparison:
class Photo < ActiveRecord::Base
#...
has_attached_file :image #, ...
before_validation_on_create :generate_md5_checksum
validate :unique_photo
#...
def generate_md5_checksum
self.md5_checksum = Digest::MD5.hexdigest(image.to_file.read)
end
def unique_photo
photo_digest = self.md5_checksum
errors.add_to_base "You have already uploaded that file!" unless User.find(self.user_id).photos.find_by_md5_checksum(photo_digest).nil?
end
# ...
end
Then I just added a column to my photos table called md5_checksum, and voila! Now my app throws a validation error if you try to upload the same photo!
No idea how efficient/inefficient this is, so refactoring's welcome!
Thanks!
What about doing an MD5 on the image file? If it is the exact same file, the MD5 hash will be the same for both images.
For anyone else trying to do this. Paperclip now has md5 hashing built in. If you have a [attachment]_fingerprint in your model, paperclip will populate this with the MD5.
Since I already had a column named hash_value, I made a 'virtual' attribute called fingerprint
#Virtual attribute to have paperclip generate the md5
def picture_fingerprint
self.hash_value
end
def picture_fingerprint=(md5Hash)
self.hash_value=md5Hash
end
And, with rails3, using sexy_validations, I was able to simply add this to the top my my model to ensure that the hash_value is unique before it saves the model:
validates :hash_value, :uniqueness => { :message => "Image has already been uploaded." }
You might run into a problem when your images have amended EXIF metadata. This happened to me, and I had to extract pixel values and calculate MD5s out of them, to ignore changes made by Wordpress etc. You can read about it on our blog: http://www.amberbit.com/blog/2013/12/20/similar-images-detection-in-ruby-with-phash/ but essentially you want to get the pixel data out of image with some tool (like RMagick), concatinate it to string, and calculate MD5 out of that.
As Stephen indicated, your biggest issue is how to determine if a file is a duplicate, and there is no clear answer for this.
If these are photos taken with a digital camera, you would want to compare the EXIF data. If the EXIF data matches then the photo is most likely a duplicate. If it is a duplicate then you can inform the user of this. You'll have to accept the upload initially though so that you examine the EXIF data.
I should mention that EXIFR is a nice ruby gem for examining the EXIF data.

Resources