The main reason I'm writing this blog post now is the struggle I've been through, when getting my Rails API to work with S3. The idea is to receive an image (through the API), in Base64 format, convert it to an image and store it locally (temporary) and upload it to S3. Sounds pretty easy, huh?
To start off, we'll add a few gems to our Gemfile:
gem 'carrierwave'
gem 'carrierwave-base64'
gem 'fog'
gem 'figaro'
gem 'unf'
Once you configure CarrierWave and CarrierWave-Base64, as well as fog, the next step is to process the received image and upload it to S3. Add the following methods in your controller:
def split_base64(uri_str)
if uri_str.match(%r{^data:(.*?);(.*?),(.*)$})
uri = Hash.new
uri[:type] = $1 # "image/gif"
uri[:encoder] = $2 # "base64"
uri[:data] = $3 # data string
uri[:extension] = $1.split('/')[1] # "gif"
return uri
else
return nil
end
end
def convert_data_uri_to_upload(obj_hash)
if obj_hash[:image_url].try(:match, %r{^data:(.*?);(.*?),(.*)$})
image_data = split_base64(obj_hash[:image_url])
image_data_string = image_data[:data]
image_data_binary = Base64.decode64(image_data_string)
temp_img_file = Tempfile.new("")
temp_img_file.binmode
temp_img_file << image_data_binary
temp_img_file.rewind
img_params = {:filename => "image.#{image_data[:extension]}", :type => image_data[:type], :tempfile => temp_img_file}
uploaded_file = ActionDispatch::Http::UploadedFile.new(img_params)
obj_hash[:image] = uploaded_file
obj_hash.delete(:image_url)
end
return obj_hash
end
Request example:
{
"user":{
"name": "John Doe",
"image_url": "data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP////8AAP///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw=="
}
}
The image_url parameter should contain the Base64-encoded image, in the format above.
Next, in your controller's create method:
def create
@user = User.new(convert_data_uri_to_upload(user_params))
if @user.save
render status: 200, json: {
message: "Successfully added user with ID: " + @user.id.to_s,
image_url: @user.image_url
}
else
render status: 422, json:{
errors: @user.errors
}.to_json
end
end
Lastly, in Fog.rb:
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => "AWS",
:region => ENV['AWS_REGION'],
:aws_access_key_id => ENV['AWS_ACCESS_KEY_ID'],
:aws_secret_access_key => ENV['AWS_SECRET_ACCESS_KEY']
}
if Rails.env.production?
config.root = Rails.root.join('tmp')
config.cache_dir = "#{Rails.root}/tmp/uploads"
end
config.fog_directory = ENV['AWS_BUCKET']
config.fog_public = false
end
Make sure you set the correct AWS Keys, Region and Permissions (I got stuck on that for a long time).
If everything went perfect, you should see something similar:
{
"message": "Successfully added user with ID: 1",
"image_url": "https://long-ass-link-to-image"
}
This entire process could be pretty catchy, so pay attention to every little detail as it could make a significant difference. If you have any questions, please don't hesitate to contact me or leave a comment in the section below. I'll be more than happy to help you!