I have a model that has:
mount_uploader :image, ImageUploader
When uploading an Image I want to retrieve some width, height and some EXIF data from the image. In a before filter I am calling self.image.url but this will return something like:
/uploads/tmp/20110630-1316-10507-7899/emerica_wildinthestreets.jpg
The problem is that when I try to open this image using:
image = MiniMagick::Image.open(self.image.url)
I get "No such file or directory - /uploads/tmp/20110630-1312-10507-6638/emerica_wildinthestreets.jpg". It seems like the image has already been moved from the tmp folder to it's final location but self.image.url is not reflecting this change.
I've also tried this in an after_save method but the result is the same. Any ideas?
Turns out I needed to append "#{Rails.root.to_s}/public/" to self.image.url
Related
I am trying to rotate an image based on the image's exif data. The image is uploaded using the django admin request.FILES. If the file is large, and is uploaded as an TemporaryUploadedFile, and I can rotate the image correctly. However, I am having an issue when the image is smaller and uploaded as an InMemoryUploadedFile.
When the image is an InMemoryUploadedFile, I can rotate the image based on the exif data and save it to disk. The image on disk is rotated correctly. When I try to save the image as an io.BytesIO() array, the rotation seems to be lost.
The following code is called from save_model in the ModelAdmin code, and takes as input the UploadedFile from request.FILES, and returns an UploadedFile to be saved in the model FileField.
def rotate_image_from_exif(f):
logger.debug("rotate_image_from_exif START")
if isinstance(f, TemporaryUploadedFile): # this image is rotated correctly in the model
# large files in TemporaryUploadedFile
with open(f.temporary_file_path(), 'rb') as image_file:
image = pImage.open(image_file)
img = ImageOps.exif_transpose(image)
if 'exif' in image.info:
exif = image.info['exif']
img.save(f.temporary_file_path(), exif=exif)
img.save(f.temporary_file_path())
return f
elif isinstance(f, InMemoryUploadedFile):
image = pImage.open(f.file)
quality = 85
temp_path = '/tmp/fred.jpg'
img = ImageOps.exif_transpose(image)
img.save(temp_path, format=image.format, quality=quality) # this image is rotated correctly on the file system
img.seek(0)
output = io.BytesIO()
img.save(output, format=image.format, quality=quality)
output.seek(0)
return InMemoryUploadedFile(output,
'FileField', 'original_image', image.format,
sys.getsizeof(output), None) # this image is not rotated
else:
# can't read file
return None
I can solve this problem by setting FILE_UPLOAD_MAX_MEMORY_SIZE to something very small (say 1000), so all uploaded files are saved as TemporaryUploadedFiles, but I would really like to solve this issue with rotating an InMemoryUploadedFile.
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.
In my Rails 4 application I have large number of images stored on S3 using Paperclip. Image url looks like http://s3.amazonaws.com/bucketname/files/images/000/000/012/small/image.jpg?1366900621.
Given following attachment class:
How can I download images from S3 and store locally ?
Then how to resize that locally stored image
Upload resized image to another S3 bucket without Paperclip (at a path s3/newbucket/images/{:id}/{imagesize.jpg})
Attachment class:
class Image < ActiveRecord::Base
has_attached_file :file, styles: { thumbnail: '320x320', icon: '64x64', original: '1080x1080' }
validates_attachment :file, presence: true, content_type: { content_type: /\Aimage\/.*\Z/ }
end
The basic advice would be not to resize images on-the-fly as this may take a while and your users may experience a huge response times during this operation. In case you have some predefined set of styles it would be wise to generate them in advance and just return back when required.
Well, here is what you could do if there is no other option.
def download_from_s3 url_to_s3, filename
uri = URI(url_to_s3)
response = Net::HTTP.get_response(uri)
File.open(filename, 'wb'){|f| f.write(response.body)}
end
Here we basically downloaded an image located at a given URL and saved it as a file locally. Resizing may be done in a couple of different ways (it depends on whether you want to serve the downloaded file as a Paperclip attachment).
The most common approach here would be to use image-magick and its convert command-line script. Here is an example of resizing an image to width of 30:
convert -strip -geometry 30 -quality 100 -sharpen 1 '/photos/aws_images/000/000/015/original/index.jpg' '/photos/aws_images/000/000/015/original/S_30_WIDTH__q_100__index.jpg' 2>&1 > /dev/null
You can find documentation for convert here, it's suitable not only for image resizing, but also for converting between image formats, bluring, cropping and much more! Also you could be intrested in Attachment-on-the-Fly gem, which seems a little bit outdated, but has some insights of how to resize images using convert.
The last step is to upload resized image to some S3 bucket. I assume that you've already got aws-sdk gem and AWS::S3 instance (the docs can be found here).
def upload_to_s3 bucket_name, key, file
s3 = AWS::S3.new(:access_key_id => 'YOUR_ACCESS_KEY_ID', :secret_access_key => 'YOUR_SECRET_ACCESS_KEY')
bucket = s3.buckets[bucket_name]
obj = bucket.objects[key]
obj.write(File.open(file, 'rb'), :acl => :public_read)
end
So, here you obtain an AWS::S3 object to communicate with S3 server, provide your bucket name and desired key, and basically upload an image with an option to make it visible to everybody on the web. Note that there are lots of additional upload options (including file encryption, access permissions, metadata and much more).
The problem is what the title says. I manage to upload an image, resize it and save the resied image to a specific folder with a different name than the name of the uploaded image.
I use this code ...
WebImage img = new WebImage(this.File.InputStream);
img.Resize(width, height).Crop(1, 1);
img.Save(dir, extenstion, true);
And it works. But when I try to save the file with HttpPostedFileBase::SaveAs(dir) method with a new filename, it saves a blank image with 0 bytes. When i try to save it as the same filename that was uploaded, it saves correctly. But, as one will think, user can save images with the same filename, so that behaviour is not desirable.
So, the question is, how to save a uploaded image with SaveAs() but with a new filename that i generate?
I tried these links
Upload tut 1
Upload tut 2
and I save searched extensivly but I couldn't find anything that would solve this.
I also tried saving the original uploaded image with WebImage class after I save the resized one but it also saves it blank.
EDIT
Code for saving the original uploaded image...
string dir = "~/Content/images/large/";
string newFilename = "2154689.jpg" // filename is generated and then added the correct extension
UploadedFile.SaveAs(Path.Combine(dir, newFilename));
This works but it saves the image with the filename uploaded by the user
UploadedFile.SaveAs(Path.Combine(dir, Path.GetFileName(this.File.FileName)));
I'm new in Asp.net and C# for about 15 days and this was a mistake on my part. You have to make the filename the sam extension as the file uploaded. So this works...
File.SaveAs(Path.Combine(dir, Path.GetFileName(newFilename + "." + ext)));
A pretty silly mistake on my part.
This is the problem. I am using Carrierwave to upload my images and now I am trying to get file size in KB for each version of my uploaded image. I've tried this but it gives me a wrong file size:
if #file
img = ::Magick::Image::read(#file.file).first
size = img.to_blob.size
end
If I remove to_blob and try only with img.size, I receive error that size is not a defined method. Is there a way to get an image size in Carrierwave uploader while uploading file? Thanks all :)
The Carrierwave wiki has a recipe for how to implement this:
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Store-the-uploaded-file-size-and-content-type