I found gem activestorage-validator, but is there a way of validating an attached image based on its dimensions?
I essentially want to be able to block a user from attaching an image that can't cleanly be cropped/compressed (i.e. 3000px x 10px).
Thanks!
Related
I have a user submission form that includes images. Originally I was using Carrierwave, but with that the image is sent to my server for processing first before being saved to Google Cloud Services, and if the image/s is/are too large, the request times out and the user just gets a server error.
So what I need is a way to upload directly to GCS. Active Storage seemed like the perfect solution, but I'm getting really confused about how hard compression seems to be.
An ideal solution would be to resize the image automatically upon upload, but there doesn't seem to be a way to do that.
A next-best solution would be to create a resized variant upon upload using something like #record.images.first.variant(resize_to_limit [xxx,xxx]) #using image_processing gem, but the docs seem to imply that a variant can only be created upon page load, which would obviously be extremely detrimental to load time, especially if there are many images. More evidence for this is that when I create a variant, it's not in my GCS bucket, so it clearly only exists in my server's memory. If I try
#record.images.first.variant(resize_to_limit [xxx,xxx]).service_url
I get a url back, but it's invalid. I get a failed image when I try to display the image on my site, and when I visit the url, I get these errors from GCS:
The specified key does not exist.
No such object.
so apparently I can't create a permanent url.
A third best solution would be to write a Google Cloud Function that automatically resizes the images inside Google Cloud, but reading through the docs, it appears that I would have to create a new resized file with a new url, and I'm not sure how I could replace the original url with the new one in my database.
To summarize, what I'd like to accomplish is to allow direct upload to GCS, but control the size of the files before they are downloaded by the user. My problems with Active Storage are that (1) I can't control the size of the files on my GCS bucket, leading to arbitrary storage costs, and (2) I apparently have to choose between users having to download arbitrarily large files, or having to process images while their page loads, both of which will be very expensive in server costs and load time.
It seems extremely strange that Active Storage would be set up this way and I can't help but think I'm missing something. Does anyone know of a way to solve either problem?
Here's what I did to fix this:
1- I upload the attachment that the user added directly to my service provider ( I use S3 ).
2- I add an after_commit job that calls a Sidekiq worker to generate the thumbs
3- My sidekiq worker ( AttachmentWorker ) calls my model's generate_thumbs method
4- generate_thumbs will loop through the different sizes that I want to generate for this file
Now, here's the tricky part:
def generate_thumbs
[
{ resize: '300x300^', extent: '300x300', gravity: :center },
{ resize: '600>' }
].each do |size|
self.file_url(size, true)
end
end
def file_url(size, process = false)
value = self.file # where file is my has_one_attached
if size.nil?
url = value
else
url = value.variant(size)
if process
url = url.processed
end
end
return url.service_url
end
In the file_url method, we will only call .processed if we pass process = true. I've experimented a lot with this method to have the best possible performance outcome out of it.
The .processed will check with your bucket if the file exists or not, and if not, it will generate your new file and upload it.
Also, here's another question that I have previously asked concerning ActiveStorage that can also help you: ActiveStorage & S3: Make files public
I absolutely don't know Active Storage. However, a good pattern for your use case is to resize the image when it come in. For this
Let the user store the image in Bucket1
When the file is created in Bucket1, an event is triggered. Plug a function on this event
The Cloud Functions resizes the image and store it into Bucket2
You can delete the image in Bucket1 at the end of the Cloud Function, or keep it few days or move it to cheaper storage (to keep the original image in case of issue). For this last 2 actions, you can use Life Cycle to delete of change the storage class of files.
Note: You can use the same Bucket (instead of Bucket1 and Bucket2), but an event to resize the image will be sent every time that a file is create in the bucket. You can use PubSub as middleware and add filter on it to trigger your function only with the file is created in the correct folder. I wrote an article on this
I have a TestimonialResource model which has a resource field type. The backend works okay where I am able to pick a file to upload as a resource to the testimonial.
However, I am unable to generate a thumbnail of the resource(if it is an image). I know it works with an image field type and have done it in the past.
I have also looked at the available methods in the Rails console and it does list thumb as a method.
However, the following does not work in the front-end view.
<%= testimonial_resource.content.thumb('400x400#').url %>
Content is the RefineryCMS resource.
Can someone point me in the right direction?
Dragonfly stores an image once and produces image variations (thumbnails, formats and enhancements) on-the-fly.
So, all related methods are available only to the image model. That means you can't run those methods on everything, but you have a link or local address to the image. So, you can always build a thumb manually:
require "mini_magick"
image = MiniMagick::Image.open("input.jpg")
image.resize "400x400"
image.format "png"
image.write "output.png"
For the existing image object:
image.thumbnail(:geometry => '400x400#c').url
I am searching the way to automatically crop image after save of Image:
I came across a tedious problem when transforming html to image in rails.
My problem is when html is converted to image then extra background is also appended to image, which I don't need so trying to crop image after save callback through some code.
I need answer specific to the problem, any help will be appriciated.
Thanks In Advance
Friends I povided the solution to answer below but I need more efficient and effective than my provided solution.
1) I want to do in shorter lines
2) With little cost of croping
3) Efficient algo
I think the most proper way is to use ImageMagic , which has its own scripting language (or API ) . Take a look at this Railscast. It uses CarrierWawe , I don't think it would be a problem for you.
After saving image I called this method to crop image
def reprocess_final_image
image_file_url = Rails.root.to_s+'/public'+self.final_image.url(:original).split('?')[0]
img = Magick::Image.read(image_file_url).first
img.format = "PNG"
img.crop_resized!(240,200, Magick::NorthWestGravity)
img.write(image_file_url)
end
where Crop resized method have parameters
img.crop_resized!(width,height,Gravity)
Cloudinary provide an awesome service for this kind of thing. Easy peasy integration with Heroku too!
I've build a processor that determine which image format is best to compress my model attached thumbnail. The processor simply build images in png and jpg and check which one is the smallest.
Since Paperclip use the original thumbnail format to build it's thumbnail style URL I had to create a field in my model to store the format of each of the styled thumbnail.
ex: thumbnail_small_content_type, would be "image/png"
In my processor I tried to save the format by using the Paperclip:attachment method : instance_write.
#attachment.instance_write "#{#style_name}_content_type", "image/#{optimised_format}"
Strangely it work perfectly when a create a new model, but failed to work when I use the paperclip method reprocess! to crop my image. Any idea how I could workaround that limitation?
I am working on maintaing an old code base and I'm migrating attachment_fu to paperclip. I migrated one thing but now I'm having a small issue.
There's a partial that renders images given the type of image and a thumbnail style. I fixed the part to render the image and that's fine, but the "else" assumes that there actually is no photo or image. I basically just want a completely detached list of the style=> geometry pairs that aren't dependent on a particular object, but I can't seem to do this without doing things like creating a new object and pulling the string from there, and even that didn't work correctly. Is there a way I could pull it straight out of paperclip or straight out of the model? The old method was using something refelect_on_association which I don't even understand... Help please. thanks :)
Paperclip has a notion of "default_url", if you specify this in your model it will attempt to pull the default url if an image isn't assigned to that object yet (your "else" case).
The default_url accepts the :style interpolations, so you can setup your style/geometry pairs in a separate folder.
Step 1
Put your default images in a directory like "/images/users/avatar/missing/".
Example file names:
missing_thumb.png
missing_small.png
Step 2
Add this line to your has_attached_file declaration in your model:
:default_url => "/images/:class/:attachment/missing/missing_:style.png"