Image Upload from Base64 code in rails

image_upload.png

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