Image Upload from Base64 code in rails
Bài đăng này đã không được cập nhật trong 3 năm
Now-a-days, use of image in web application has become an attractive part. The most famous example is FaceBook. It has become a trend to upload photos, change profile picture, cover photo etc. and collect loads of likes and comments. To do the same in rails, you can use the most popular gem carrierwave and it is easy to use and configure. You can learn about it from here. Usually in rails, image is sent as raw form-data to be processed through carrierwave. But what will happen if your fronend guy say that they will send you Base64 code of image in stead raw image? Don't be panic beacause it is not the end of the world. There is a way out
Base64
Some days ago i was working on server side of a swift application. There was a portion of changing profile picture of an user and my swift teammate said that he would implement it in swift way i.e, he would send me base64 code of an image instead of formdata. The first thing that knocked my mind was what base64 is!! Base64 is a type of number encoding scheme that represent data in ASCII- string format by translating into radix-64 representation. Radix-n is representing a string by only n symbols. Like decimal is radix-10 translation which uses digit 0-9.
Solution
There is a Base64 and StringIO library in ruby, which will help to solve this problem. Suppose we have a User
model which has attributes name and avatar. So we will mount carrierwave uploader with this like the above tutorial. Now how it works, we can see it by running some code in rails console. First we will create a new object of User class by this
user = User.new(name: "Jolil")
Now with the help of File library, we can convert our image to machine readable format with this
image = File.open("/home/Pictures/dream.png") {|img| img.read}
we will see some machine code like this
\x89PNG\r\n\u001A\n\u0000\u0000\u0000\rIHDR\u0000\u0000\u0001\xF0\u0000\u0000\u0001\xF0\b\u0002\u0000\u0000\u0000\xD6Ϊ\xB1\u0000\u0000\u0000\x85zTXtRaw profile type exif\u0000\u0000x\xDAu\x8E\xCB\t\u00041\fC\xEF\xA9bK\xF0/\xFE\x94\xB3\f\u0013\x98\u000E\xA6\xFC\xB5IBN\xFB\u000E\xB2\u0010F\xA8\xDD\xEF3ڧ@\xA0&\xDD\\C\u0015\u0012\t\t\xFA\xA6q\x980\u0000\u0012`\xDD\xD4ɺ......
So we will convert this to base64 encoded format using encode64
method of Base64 library. We will run
encoded_image = Base64.encode64 image
we now can see the base64 conversion of our image which is like this
iVBORw0KGgoAAAANSUhEUgAAAfAAAAHwCAIAAADWzqqxAAAAhXpUWHRSYXcg\ncHJvZmlsZSB0eXBlIGV4aWYAAHjadY7LCQQxDEPvqWJL8C/+lLMME5gOpvy1\n.......
Ok our fronend part is complete. So we now have to convert back to total image with the use of StringIO
and decode64
method. Usually in carrierwave, stored image needs a name and it uses the sent image filename(with some processing). But we are using encode, so it do not have any name. We have to manually add a name when after decoding. For that there is two way out. They are:
Dirty way
One way is to use the Strinstream using StringIO and use inline method like this(with output)
pry(main)> io = StringIO.new(Base64.decode64(encoded_image))
=> #<StringIO:0x00000005324d08>
pry(main)> def io.original_filename; "pic.jpg"; end
=> :original_filename
Its not recommended way as using inline method and nested method(when you use this in controller) is not good rails coding practice.
Clean way
and other way and recommended way is to write a StrinIO extended file and define the original_filename method in it. So this file will de like
avatar_string_io.rb
class AvatarStringIO < StringIO
def original_filename
"pic.jpg"
end
end
Now like the first way, we just have to use this instead of StringIO (Perhaps its a StringIO class just extended), which is
pry(main)> io = AvatarStringIO.new(Base64.decode64(encoded_image))
=> #<AvatarStringIO:0x000000059cdee8>
Now all you have to do is to assign this io string to the avatar attribute of url and save this record by
user.avatar = io
user.save
It succeded, isn't it? Now go to user.avatar.url. You can see the image you wished to save as avatar. Question may arise we see in console but what to write in application. Its pretty simple. we have to add just one of above two ways. If frontend send you base64 code in avatar param, then in controller,
Dirty way
io = StringIO.new(Base64.decode64(param[:avatar]))
def io.original_filename; "pic.jpg"; end
user.update_attributes avatar: io
or
clean way
io = AvatarStringIO.new(Base64.decode64(param[:avatar]))
user.update_attributes avatar: io
Ok try this out in your application if you need.
ha5
All rights reserved