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 - ruby-on-rails

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

Related

Rails ActiveStorage: Attach a Vips Image

I'm struggling to attach a Vips:Image object to an ActiveStorage object.
I use Vips to compress a PNG image. I'm then looking to save this compressed PNG version into a second ActiveStorage attachment. The code is failing when attempting to attach. All I get is: Process finished with exit code -1073741819 (0xC0000005)
# compress the image and save the compressed version of the file as a PNG
# both img and img_to_compress are active storage attachments
def compress_charlie(img, img_to_compress)
# load the image as a Vips Image object
vips_img = Vips::Image.new_from_buffer (URI.open(img.url) { |f| f.read }), ''
# do compression, etc ... not bothering to show this code as it has no impact on the issue I have
# save the compressed png
img_to_compress.attach(
io: StringIO.new(vips_img .write_to_buffer('.png')),
filename: img.filename
)
end
Any help is appreciated, Charlie
Ruby 3.1
Rails 7.0.1
You're decompressing and recompressing twice. How about (untested):
def convert_to_png(img, img_to_compress)
# load the image as a Vips Image object
vips_img = Vips::Image.new_from_buffer (URI.open(img.url) { |f| f.read }), ''
# save as a PNG string
png = StringIO.new(vips_img.write_to_buffer('.png')),
img_to_compress.attach(io: png, filename: img.filename)
end
pngsave_buffer gives you a String with binary encoding, you don't need to save a second time. Try this test program:
require "vips"
x = Vips::Image.new_from_file ARGV[0]
y = x.pngsave_buffer
puts "y.class = #{y.class}"
I see:
$ ./pngsave.rb ~/pics/k2.jpg
y.class = String
If the image is already a PNG you'll be wasting a lot of time, of course. You could add something to detect the format and skip the conversion.

Rails save file as image

I use webdav protocol. gem 'net_dav' it's old but usefull
when i get file through it i receive it like this \xFF\xD8\xFF\xE0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xFF\...
How to convert to real image .jpg or .png?
you will have to decode this base64 string and then save file, something like
File.open('file_name.jpg', 'wb') do|f|
f.write(Base64.decode64(base_64_encoded_string))
end

Base64 Encode Image and Save it later to a file

I have an application where an admin can upload an image. I save the image in a file and also base64 encode (using Base64.strict_encode method of ruby) & save in my DB. This is so that when later someone deleted the physical file from the HDD/Server, I can still generate it back by base64 decoding it (Base64.decode method) and save in a file.
But the encoding and decoding didn't go well as the image get damaged and I'm unable to view it after save.
I checked the output of the Base64.strict_encode against the result when I used http://www.base64-image.de/ to encode the file, they were different.
Can anyone help me with this? What am I doing wrong? What am I not doing?
ENCODING THE IMAGE DURING UPLOAD:
imageLoc = image.image.to_s
logger.info '>>>>>>' + (Base64.strict_encode64(open(imageLoc).read)).to_s
image_data = Base64.strict_encode64(File.open(imageLoc, 'rb').read)
CategoryImage.update_image_data(image.id,image_data)
DECODING WHEN IMAGE FILE IS LOST:
File.open(File.join(APP_CONFIG['image_storage_location'], image[:image]), 'wb') { |f|
content = image[:image_data]
content.gsub!('\\r', "\r")
content.gsub!('\\n', "\n")
f.write(Base64.decode64(content))
f.close
}
ENCODED IMAGE FROM THE SITE (base64-image.de): https://shrib.com/cYLKfEe1?v=nc
ENCODED IMAGE FROM MY CODE: https://shrib.com/CODE-encoded%20image?v=nc
EDIT
When I replaced the encoded image data in my DB with the one I generated from the above named website, my image was regenerated and viewable. So the real is with the encoding.
Had once a similar issue, solved it by replacing the File.read method with IO.binread(imageLoc). Hope it helps. :)

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)

Converting PDFs to PNGs with Dragonfly

I have a Dragonfly processor which should take a given PDF and return a PNG of the first page of the document.
When I run this processor via the console, I get back the PNG as expected, however, when in the context of Rails, I'm getting it as a PDF.
My code is roughly similar to this:
def to_pdf_thumbnail(temp_object)
tempfile = new_tempfile('png')
args = "'#{temp_object.path}[0]' '#{tempfile.path}'"
full_command = "convert #{args}"
result = `#{full_command}`
tempfile
end
def new_tempfile(ext=nil)
tempfile = ext ? Tempfile.new(['dragonfly', ".#{ext}"]) : Tempfile.new('dragonfly')
tempfile.binmode
tempfile.close
tempfile
end
Now, tempfile is definitely creating a .png file, but the convert is generating a PDF (when run from within Rails 3).
Any ideas as to what the issue might be here? Is something getting confused about the content type?
I should add that both this and a standard conversion (asset.png.url) both yield a PDF with the PDF content as a small block in the middle of the (A4) image.
An approach I’m using for this is to generate the thumbnail PNG on the fly via the thumb method from Dragonfly’s ImageMagick plugin:
<%= image_tag rails_model.file.thumb('100x100#', format: 'png', frame: 0).url %>
So long as Ghostscript is installed, ImageMagick/Dragonfly will honour the format / frame (i.e. page of the PDF) settings. If file is an image rather than a PDF, it will be converted to a PNG, and the frame number ignored (unless it’s a GIF).
Try this
def to_pdf_thumbnail(temp_object)
ret = ''
tempfile = new_tempfile('png')
system("convert",tmp_object.path[0],tmpfile.path)
tempfile.open {|f| ret = f.read }
ret
end
The problem is you are likely handing convert ONE argument not two
Doesn't convert rely on the extension to determine the type? Are you sure the tempfiles have the proper extensions?

Resources