I had a regular file upload that I now changed to using Cloudinary.
Upon upload I did the following to prevent orientation glitches when uploading images from a mobile device (See exif image rotation issue using carrierwave and rmagick to upload to s3 for details):
process :rotate
process :store_dimensions
def rotate
manipulate! do |image|
image.tap(&:auto_orient)
end
end
def store_dimensions
# This does not work with cloudinary #18
if file && model
model.width, model.height = ::MiniMagick::Image.open(file.file)[:dimensions]
end
end
Neither rotation nor storing the dimensions work, since I switched to cloudinary.
Now Cloudinary has an official tutuorial that shows how to do this but it simply does not work and other people seem to have the same issue and neither of the provided options worked for me:
I was able to get it working using a variation of the first option:
after_save :update_dimensions
def update_dimensions
if self.image != nil && self.image.metadata.present?
width = self.image.metadata["width"]
height = self.image.metadata["height"]
self.update_column(:width, width)
self.update_column(:height, height)
end
end
Important: since we're inside a after_save callback here it's crucial to use update_column so that we don't trigger another callback and end up in an infinite loop.
Fix for the provided solution:
self.image.present? returned false but self.image != nil returend true.
Related
I'm using MiniMagick to resize my image. My method looks something like that:
def resize
mini_img = MiniMagick::Image.new(img.tempfile.path)
mini_img.combine_options do |c|
c.resize '50x50^'
c.gravity 'center'
c.extent '50x50'
end
mini_img
end
Resize works, but the problem is when I try save mini_img to Active Storage, because I get error Could not find or build blob: expected attachable, got #<MiniMagick::Image. Can I somehow convert MiniMagick::Image (mini_img) to normal image and save it into Active Storage?
Yes, you can. Currently you are trying to save an instance of MiniMagick::Image to ActiveStorage, and that's why you receive that error. Instead you should attach the :io directly to ActiveStorage.
Using your example, if you wanted to attach mini_img to a hypothetical User, that's how you would do it:
User.first.attach io: StringIO.open(mini_img.to_blob), filename: "filename.extension"
In this example I am calling to_blob on mini_img, which is an instance of MiniMagick::Image, and passing it as argument to StringIO#open. Make sure to include the :filename option when attaching to ActiveStorage this way.
EXTRA
Since you already have the content_type when using MiniMagick you might want to provide it to ActiveStorage directly.
metadata = mini_img.data
User.first.attach io: StringIO.open(mini_img.to_blob), filename: metadata["baseName"], content_type: metadata["mimeType"], identify: false
For someone who will have a similar problem. I just changed method to:
def resize
MiniMagick::Image.new(img.tempfile.path).combine_options do |c|
c.resize '50x50^'
c.gravity 'center'
c.extent '50x50'
end
img
end
In this solution, MiniMagick only resized the photo and did nothing else with it, so I didn't have to convert it again.
I'm following this article in CarrierWave Wiki https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Add-more-files-and-remove-single-file-when-using-default-multiple-file-uploads-feature to implement adding more images and removing images for a model in my system using CarrierWave Multiple Uploads feature.
The main code in that article is
def add_more_images(new_images)
images = #gallery.images
images += new_images
#gallery.images = images
end
def remove_image_at_index(index)
remain_images = #gallery.images # copy the array
deleted_image = remain_images.delete_at(index) # delete the target image
deleted_image.try(:remove!) # delete image from S3
#gallery.images = remain_images # re-assign back
end
It works. However, it is too slooooow. I have looked at the log and the overall processing time is as follow:
Upload 1 image: it takes 5000ms for example
Add 1 more image: it takes 8500ms (2 images)
Add 1 more image: it takes 12000ms (3 images)
Remove 1 image: it takes 8400ms (back to 2 images)
I have tested the sample app of this solution written by the author on my local machine and it was very slow too.
It seems like CarrierWave reuploads and re-processes all images even we only add or remove 1 image. I think because we are re-assigning back new array of images to #gallery so that it treats old images as new ones.
Also there is a related issue here https://github.com/carrierwaveuploader/carrierwave/issues/1704#issuecomment-259106600
Does anyone have any better solution for adding and removing images using CarrierWave multiple upload feature?
Thanks.
when you call model.images = remain_images, carrierwave will upload all images. So the more images you stored in a column, the longer it will take.
See: mount.rb#L300, mounter.rb#L40
I had this problem before, the following is my code:
new_images = self.logo_images.clone
4.times do |t|
next if !(image = params[:"logo_image#{t + 1}"])
new_images[t] = image
changed = true
end
self.logo_images = new_images if changed
...
self.save if changed
And this is the hack...
(works fine with carrierwave 1.0.0 and carrierwave-aws 1.1.0)
mounter = self.send(:_mounter, :logo_images)
4.times do |t|
next if !(image = params[:"logo_image#{t + 1}"])
uploader = mounter.blank_uploader
uploader.cache!(image)
mounter.uploaders[t] = uploader
changed = true
end
mounter.uploaders.each{|s| s.send(:original_filename=, s.file.filename) if !s.filename} if changed
...
self.save if changed
Using the Refile gem to handle file uploading in Rails, what is the best way to determine image height and width during / after it has been uploaded? There is no built in support for this AFAIK, and I can't figure out how to do it using MiniMagick.
#russellb's comment almost got me there, but wasn't quite correct. If you have a Refile::File called #file, you need to:
fileIO = #file.to_io.to_io
mm = MiniMagick::Image.open(fileIO)
mm.width # image width
mm.height # image height
Yes, that's two calls to #to_io >...< The first to_io gives you a Tempfile, which isn't what MiniMagick wants. Hope this helps someone!
-- update --
Additional wrinkle: this will fail if the file is very small (<~20kb, from: ruby-forum.com/topic/106583) because you won't get a tempfile from to_io, but a StringIO. You need to fork your code if you get a StringIO and do:
mm = MiniMagick::Image.read(fileio.read)
So my full code is now:
# usually this is a Tempfile; but if the image is small, it will be
# a StringIO instead >:[
fileio = file.to_io
if fileio.is_a?(StringIO)
mm = MiniMagick::Image.read(fileio.read)
else
file = fileio.to_io
mm = MiniMagick::Image.open(file)
end
Refile attachments have a to_io method (see Refile::File docs) which returns an IO object that you can pass to MiniMagick.
Assuming you have an Image model with a file attachment (id stored in a file_id string column) and width and height columns you can use the following callback:
class Image < ActiveRecord::Base
attachment :file
before_save :set_dimensions, if: :file_id_changed?
def set_dimensions
image = MiniMagick::Image.open(file.to_io)
self.width = image.width
self.height = image.height
end
end
Hope that helps.
You can use MiniMagick to do this (but need to be using the latest version).
image = MiniMagick::Image.open('my_image.jpg')
image.height #=> 300
image.width #=> 1300
This is all pretty well documented in the README.md for the gem: https://github.com/minimagick/minimagick
Versions:
Ruby 2.2.3
Rails 4.2.4
mini_magick: 4.2.10
Carrierwave 0.10.0
Description
I am trying to create a watermarker for a small gallery using CarrierWave as uploader.
I want the watermark to be sized compared to the current image. Therefore I am trying to use a .svg-file with different opacities and a transparent background.
I am using a watermarker based on Carrierwave add a watermark to processed images
require 'mini_magick'
class Watermarker
def initialize(original_path, watermark_path)
#original_path = original_path.to_s
#watermark_path = watermark_path.to_s
end
def watermark!(options = {})
options[:gravity] ||= 'SouthEast'
image = MiniMagick::Image.open(#original_path)
watermark = MiniMagick::Image.open(#watermark_path)
result = image.composite(watermark, 'png') do |c|
c.gravity options[:gravity]
end
result.write #original_path
end
end
And calling this as a process from my uploader.
My problems:
I cannot get the watermarker to input the picture with transparent background. I played around with:
https://github.com/minimagick/minimagick#composite
http://www.imagemagick.org/script/composite.php
But no progress.
I cannot adjust the size of the overlay image properly. There is a lot of settings for the geometry command but I'm stuck.
Any ideas and help would be great.
I've got picture uploads working with s3 on heroku no problem.
I also have a method that enables users to upload from a web-address.
Unfortunately, it seems as though when pictures are uploaded using this method they are saved without their file extension.
So I get this sort of link for image urls...
http://s3.amazonaws.com/mysite/images/23/original.?1311799466
If the same image had been uploaded locally it would look like this:
http://s3.amazonaws.com/mysite/images/23/original.JPG?1311799466
In my pic model this is the code I'm using to allow uploads via web addresses:
def download_remote_image
begin
self.image = open(URI.parse(self.pic_url))
rescue
errors.add_to_base("- something is wrong with the image url.")
return false
else
return true
end
end
Any ideas?
Change with this:
def download_remote_image
begin
io = open(URI.parse(pic_url))
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
rescue
end
end
Careful, recent versions of Paperclip throw errors when encountering a io object instead of File, not sure if they fixed that.
since you are using paperclip the picture url should be generated with paperclip such as:
self.image(:thumb)