How to use carrierwave's from_version to reference the processed base image, rather than the unprocessed one - carrierwave

I am working on a site which needs to have a newsletter page, with thumbnails. The PDFs are stored on a different site, but that site does not have thumbnails, so I must generate them myself and link the thumbnail to that site.
I cannot store the original PDF — the linked version is canonical, and having out-of-date cached versions is unacceptable. The additional storage usage might also cause an extra monthly expense for the non-profit organisation the site is for, so I do not want to leave these lying around when they're not needed.
I have the following code:
class DocumentUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def content_type_whitelist
'application/pdf'
end
def full_filename(for_file)
parent_name = super(for_file)
return unless parent_name
extension = '.pdf'
base_name = parent_name.chomp(extension)
if version_name
base_name = base_name[version_name.size.succ..-1]
end
[base_name, version_name].compact.join("_") + '.png'
end
# Process files as they are uploaded:
process convert_pdf_front: [500, 500]
version :medium do
process convert_pdf_front: [160, 160]
end
version :small do
process convert_pdf_front: [80, 80]
end
private
def convert_pdf_front(width, height)
manipulate! do |img|
img.format('png') do |c|
c.resize "#{width}x#{height}"
end
img
end
end
end
This works as expected, but the medium and small versions are converted from the original PDF, making for a rather slow process.
If I replace the processing code with this:
version :full do
process convert_pdf_front: [500, 500]
end
version :medium, from_version: :full do
process convert: 'png'
process resize_to_fit: [160, 160]
end
version :small, from_version: :full do
process convert: 'png'
process resize_to_fit: [80, 80]
end
then the medium and small versions are converted from the full version, but the original PDF is left lying around.
Is there a way to use from_version to do the conversion from the converted original document — that is, from the 500x500 PNG rather than the PDF — that doesn't leave the original PDF cluttering the place up? Is there a special version name that points to the original document after it's gone through any process commands, rather than before?
(I would rather not simply have a reaper process nuke the PDFs later, as Carrierwave regards those as the originals. I'd like Carrierwave to treat the 500x500 PNG as the original in all cases and never store the PDF once it's done with the conversion process.)

Related

Bad image quality with CarrierWave

I want to create the ability for the user to upload an image into a pdf which will be generated inside of the app. To do so I'm using gem prawn to pdf creation and CarrierWave::MiniMagic to create thumbnail versions of images for a Rails 6 app, and I've noticed loss of quality when the images are downsized.
original image:
the same image inside of pdf:
Such a bad quality is not acceptable for me. I don't know if it's a common problem but I've found a few posts without any resolution, is there any alternative solution/tool to handle it? I have been struggling with this problem for weeks.
I don't have nothing special in my uploader file:
class LogoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
EXTENSION_WHITELIST = %w[jpg jpeg png].freeze
storage :file
process convert: 'png'
version :thumb do
process resize_to_fit: [300, 300]
end
def extension_whitelist
EXTENSION_WHITELIST
end
end

Rails 4, carrierwave, minimagick: image version called every image load? (even not on uplaoad)

i have a question/problem - im not sure actually, maybe this is how it should behave(?)
I'm using Rails 4 with carrierwave to upload files, I just added the minimagick gem to create versions of the uploaded images.
when i upload image - it works great and make the version.
but when i load a page with images it seems to call the "version" method for every image that is displayed in the screen.... (and its not the upload process! only showing images!)
So even before i uploaded an image and actually created a version of it -> i've added a 'version' method to my uploader file -> refreshed a page in my site -> 'version' method was called (didnt upload anything yet)
my uploader file:
class PhotoUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :fog
version :nav_thumb, if: :is_avatar? do
process :resize_to_fit => [50, 50]
end
version :box_thumb, if: :is_logo? do
process :resize_to_fit => [40, 40]
end
def is_avatar?(picture)
mounted_as == :avatar
end
def is_logo?(picture)
mounted_as == :logo
end
end
it seems that every image displayed in my page is getting to the is_avatar? method (i used pry debugger to see whats going on)
and -> it makes my page load slower, which is the opposite of what i wanted to achieve with smaller image versions.
is this normal behaviour? should it go to the version methods every time i load an image?
what should i do to fix it?
thanks.
I think the issue results from stuffing the logic inside your version methods. Instead of writing if statements inside them, try changing them to these:
version :nav_thumb do
process resize_to_fit: [50,50]
end
version :box_thumb do
process resize_to_fit: [40,40]
end
and then write an additional method to determine which image version should be used (and then implement the result in your view with image_tag image.image_url(:version)).
Try putting your is_*? methods in a protected block:
...
version :nav_thumb, if: :is_avatar? do
process :resize_to_fit => [50, 50]
end
protected
def is_avatar?(picture)
mounted_as.eql?(:avatar)
end

Upload image default image to s3 with carrierwave

We are using carrierwave gem to upload our images on a rails project.
We need to support the creation of the object without an image, but the application will run on several platforms and we don't want to support objects without images in everyone o that platforms, so we want to have a default image.
So this was the first solution
class IconUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"uploads/#{model.id}"
end
def default_url
"/images/icons/" + [version_name, "default.png"].compact.join('_')
end
version :thumbnail do
process resize_to_fill: [200, 200]
end
version :high_resolution do
process resize_to_fill: [400, 400]
end
end
The problem with this is that all the devices will come to the server (not to S3) looking for that default image.
At the same time its a problem with the multiple versions of the image.
So this second solution came:
class CategoryController < ApplicationController
...
def create
#category = Category.new(category_params)
#uploader = IconUploader.new(#category, :icon)
uploader.cache!(File.open("app/assets/images/photos/picture.jpg"))
#category.image=#uploader
#object.save
end
...
end
This solves the problem of the multiple resolutions and it always goes to S3 to get the image. But the problem here is that it will duplicate the default image on S3 many times.
Our third solution could be a to use an already created image on Amazon as default URL, but it has the same problem with the version so we don't even implement it.
Can any one think of a better solution for this problem?

Duplicating Carrierwave images makes them darker?

I have an Event model that has many photographs. I have an image uploader mounted to the Photographs attribute, and regular uploads and everything is working fine.
However, when I try and duplicate an event, recreating a new photograph object for the new event, the new image is darker than the original, and if I duplicate the duplicate event, it gets darker still.
I have played around with it, but have no solution.
My Uploader code:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include CarrierWave::Processing::RMagick
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def cache_dir
"#{Rails.root}/tmp/carrierwave"
end
process :colorspace => :rgb
# Remove EXIF data
process :strip
# Create different versions of your uploaded files:
version :thumb do
process :resize_to_limit => [640, 640]
end
version :preview_thumb do
process :resize_to_limit => [600, 600]
end
version :wine_thumb do
process :resize_to_limit => [160, 440]
end
version :logo_thumb do
process :resize_to_limit => [90, 90]
end
end
And my duplcation code (in Active Admin):
member_action :create_duplicate_event, method: :post do
old_event = Event.find(params[:id])
photograph_urls = old_event.photographs.map(&:image_url)
attributes = old_event.attributes.except("photographs", "id")
new_photos = []
photograph_urls.each do |photo|
new_photo = Photograph.new({
remote_image_url: photo
})
if new_photo.save
new_photos << new_photo
end
end
#event = Event.new(attributes)
#event.photograph_ids = new_photos.map(&:id)
render "/admin/events/_events_form/"
end
The :rgb tag was an attempt to fix. But no luck.
Ruby 2.1 and Rails 4.0
Ok, after a lot of playing around and searching I managed to fix this problem.
First, you need to download the .icc color profiles, which can be found here. It says for windows but they seemed to work for me on my Mac.
Once you have put the .icc files into a /lib/color_profiles directory, add the following code to your uploader:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
process :convert_image_from_cmyk_to_rgb
#versions, and any other uploader code go here
def convert_image_from_cmyk_to_rgb
manipulate! do |image|
if image.colorspace == Magick::CMYKColorspace
image.strip!
image.add_profile("#{Rails.root}/lib/USWebCoatedSWOP.icc")
image.colorspace == Magick::SRGBColorspace
image.add_profile("#{Rails.root}/lib/sRGB.icc")
end
image
end
end
end
This converts CMYK images to RGB, and keeps the profiles keeping nice, while keeping RGB images as they were, and not ruining them.
I hope this helps someone in the future, and saves them the hours I spent working this out.

With Carrierwave and Rails 3 is it possible to nicely manage image and non-image files with the same uploader?

In my Rails app I would like to allow users to upload either image or non-image files via Carrierwave. Currently Carrierwave is working fine handeling and processing image files, but unfortunately its dropping non-images files completely. Is there a clean way for a single Carrierwave uploader to process both image and non-image files?
I'll include my current uploader below:
class AssetUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
version :thumb do
process :resize_to_fill => [300, 300]
end
version :icon do
process :resize_to_fill => [48, 48]
end
def extension_white_list
%w(jpg jpeg gif png pdf doc xls docx xlsx ppt)
end
end
I had this exact problem. I solved it with the hoary comp sci solution of one-more-level-of-indirection: a trampoline/thunk method that dynamically decides whether to process based on file extension.
You can find the implementation here: https://gist.github.com/995663
(the naive approach of introducing logic in the version block actually doesn't work because of how the CarrierWave DSL works - the logic needs to be deferred until invocation)
I hope that helps.
I know this has been answered, but aaron's answer doesn't completely solve the problem. You can use what he suggested at https://gist.github.com/995663 if you only need to call process, but not if you want to selectively process with version.
For version, see the carrierwave wiki page here: https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Do-conditional-processing
You can now do this in your code, and it will only process the version block on images
version :thumb, :if => :image? do
process ...
end
protected
def image?(new_file)
new_file.content_type.include? 'image'
end

Resources