How to read and upload ByteArray image in rails? - ruby-on-rails

We are having Rails in backend and FLASH in frontend.
We wanted to upload image but we don't have any physical image we have pixes in ByteArray.
Now We want to upload that ByteArray(image) into rails through API.
How can we read that ByteArray image and convert into original image and upload to rails server. For image upload we are using carrierwave gem and rmagic in server.
Sample ByteArray image:
xÚ_# ¿PNG
IHDR,,y}u¼4IDATxÚì½i$Iv-ûæ¾{¸Ǿ瞕UUµu÷$úÄm8CÎÎ
x¾ëWêHzOo¶ޗ!G

# arr is ByteArray. 'image' is still in memory
image = Magick::Image.constitute(width, height, "RGB", arr.flatten)

Related

Is there a way to convert binary data into a data type that will allow ActiveStorage to attach it as an image to my User model

I am hitting an api to get an image that they have stored and use it as the profile pic for our application's users. I'm using Ruby on Rails and ActiveStorage with AWS to attach and store the image. What they send back is this:
{"status"=>"shared", "values"=>[{"$objectType"=>"BinaryData", "data"=>"/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAAT.....KK5tT/9k=", "mime_type"=>"image/jpeg", "metadata"=>{"cropped"=>false}}]}
I tried a lot of different ways to attach it and manipulate the data such as just attaching it as it is, Base64.decode64, Base64.encode64. I also tried creating a new file and then attaching that. Here are some examples:
data = Base64.decode64(Base64.encode64(response[:selfie_image]["values"][0]["data"]))
user.profile_pic.attach!(
io: StringIO.new(open(data).read),
filename: "#{user.first_name}_#{user.last_name}_#{user.id}.jpg",
content_type: "image/jpeg"
)
data = Base64.decode64(Base64.encode64(response[:selfie_image]["values"][0]["data"]))
out_file = File.new("#{user.first_name}_#{user.last_name}_# . {user.id}.jpg", "w")
out_file.puts(data)
out_file.close
user.profile_pic.attach(
io: StringIO.new(open(out_file).read),
filename: "#{user.first_name}_#{user.last_name}_#{user.id}.jpg",
content_type: "image/jpeg"
)
I also tried:
user.profile_pic.attach(out_file)
It keeps either saying attachment is nil or depending on how I manipulate the data it will say not a jpeg file content header type wrong and throw that as an image magick error.
How can I manipulate this data to be able to attach it as an image for our users with ActiveStorage?
To get this to work I had to add gem "mini_magick" to my gemfile and then use it to decode the image data I was receiving from the api call and then turn it into a blob so that ActiveStorage could handle it.
data = response[:selfie_image]["values"][0]["data"]
decoded_data = Base64.decode64(data)
image = MiniMagick::Image.read(decoded_data)
image.format("png")
user.profile_pic.attach(
io: StringIO.new(image.to_blob),
filename: "#{user.id}_#{user.first_name}_#{user.last_name}.png",
content_type: "image/jpeg"
)
In command line ImageMagick you can use the inline: feature to decode base64 data into a gif or jpg. The base64 image here has transparency, it is most proper to save to gif or png.
convert 'inline:data:image/gif;base64,
R0lGODlhIAAgAPIEAAAAAB6Q/76+vvXes////wAAAAAAAAAAACH5BAEAAAUALAAA
AAAgACAAAAOBWLrc/jDKCYG1NBcwegeaxHkeGD4j+Z1OWl4Yu6mAYAu1ebpwL/OE
YCDA0YWAQuJqRwsSeEyaRTUwTlxUqjUymmZpmeI3u62Mv+XWmUzBrpeit7YtB1/r
pTAefv942UcXVX9+MjNVfheGCl18i4ddjwwpPjEslFKDUWeRGj2fnw0JADs=
' b64_noseguy.gif
But you can still save it to jpg. The transparency will be lost and the background will be black.
convert 'inline:data:image/jpeg;base64,
R0lGODlhIAAgAPIEAAAAAB6Q/76+vvXes////wAAAAAAAAAAACH5BAEAAAUALAAA
AAAgACAAAAOBWLrc/jDKCYG1NBcwegeaxHkeGD4j+Z1OWl4Yu6mAYAu1ebpwL/OE
YCDA0YWAQuJqRwsSeEyaRTUwTlxUqjUymmZpmeI3u62Mv+XWmUzBrpeit7YtB1/r
pTAefv942UcXVX9+MjNVfheGCl18i4ddjwwpPjEslFKDUWeRGj2fnw0JADs=
' b64_noseguy.jpg
See https://imagemagick.org/Usage/files/#inline
Sorry, I do not know the equivalent in RMagick

Rails: Download images from S3, resize and upload back to S3

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).

Read image from s3 server and process it from rails

I have a image in s3 bucket and url to access it.
I want to read the image from the s3 and to create a thumbnail icon and push the thumbnail_icon to s3.
If the image is in local, I can read the image and convert it to StringIO. After that I can push the StringIO to create thumbnail image in s3:
item = File.read(url)
data_io = StringIO.new(item)
s3_connection.interface.put(data_io, ...)
how can I open remote file and process it?
File.open(remote_url) returns No such file or directory
with OpenURI I can read the file. But I couldn't convert it to StringIO
response = open(remote_url) #Tempfile
data_io = StringIO.new(response)
#can't convert Tempfile into String`
What am I missing ?
The StringIO initialize method expects a string as the only parameter. The object you are giving it is a Tempfile. Try this:
data_io = StringIO.new(response.read)

How to get uploaded image file size?

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

Where do the temp files go when using MiniMagick in a Ruby on Rails app?

I'm using MiniMagick to perform some image resizing on images uploaded through a multi-part form. I need to generate a few different types of images from the originally uploaded file. Here's the code that's performing the image processing:
// Generates a thumbnail image
mm = MiniMagick::Image.open(Rails.root.join('public', 'uploads', new_url))
mm.resize(thumbnail_dimensions.join("x"))
mm.write(Rails.root.join('public', 'uploads', "t_"+new_url))
// Generates cropped version
mm_copy = MiniMagick::Image.open(Rails.root.join('public', 'uploads', new_url))
mm_copy.crop('200x200')
mm_copy.write(Rails.root.join('public', 'uploads', "c_"+new_url))
new_url is the path to the image in the public folder. The thumbnail routine works perfectly. When the app goes to start processing the cropped version, that is where things start breaking and I can't for the life of me figure it out. I receive the following error when from this code:
No such file or directory - /tmp/mini_magick20110627-10055-2dimyl-0.jpg
I read some stuff about possible race conditions with the garbage collector in Rails but I wasn't able to resolve the issue. I tried this from the console as well and can create MiniMagick instances but receive the No such file error there as well. At this point, I have no idea where to go so I'm hoping someone here has some helpful suggestions. Thanks for your help!
Details:
OS: Ubuntu (Lucid Lynx)
Rails Version: 3.0.7
Ruby Version: 1.8.7
MiniMagick Version: 3.3
Did you installed ImageMagick?
If not,
try sudo apt-get install ImageMagick,
and then restart your webrick server
it's probably the race condition which is mentioned here:
https://ar-code.lighthouseapp.com/projects/35/tickets/6-race-condition-with-temp_file
here's one fix:
http://rubyforge.org/tracker/index.php?func=detail&aid=9417&group_id=1358&atid=5365
alternatively, and probably easier, you could try this:
// Generates a thumbnail image
mm = MiniMagick::Image.open(Rails.root.join('public', 'uploads', new_url))
mm_copy = mm.clone # clone the opened Image, instead of re-opening it
mm.resize(thumbnail_dimensions.join("x"))
mm.write(Rails.root.join('public', 'uploads', "t_"+new_url))
// Generates cropped version
mm_copy.crop('200x200')
mm_copy.write(Rails.root.join('public', 'uploads', "c_"+new_url))

Resources