07 4 / 2012
Uploading directly to Cloudinary, bypassing your Rails app
IMPORTANT UPDATE:
cl_form_tagandvalid_cloudinary_response?are now part of cloudinary 1.0.6+ gem, so you can use them “out of the box”.
For those of you who haven’t heard about Cloudinary, check it out! Cloudinary provides everything you (as developer) would ever need for managing images. It basically works like this: you upload an image, then you request transformation of that image from the server. The transformed image is generated on the fly (unless cached) and served to you. There are plenty of transformations like: resizing, cropping, setting quality, changing file format… etc. And, if your designer ever wanted to scale thumbnails a bit, you wouldn’t need to reprocess thousands of images again!
Cloudinary has a ruby gem that can be used in Rails app and it even supports CarrierWave if you use that to manage your uploads. However all these approaches require your server to handle file upload process. Here is how it looks:
- User uploads a photo to your server
- Rails application receives the file
- Rails application sends the file to Cloudinary
Rails is the middleman and it needs to do some relatively heavy processing in order to decode the data received from the user, and prepare it for upload to the Cloudinary. From scaling aspect, this is something you’d probably like to avoid.
It turns out that it is really easy to let your users upload files directly to Cloudinary. After reading some documentation and browsing through source code, I came up with cl_form_tag helper method:
def cl_form_tag(callback_url, options={}, &block)
form_options = options.delete(:form) || {}
form_options[:method] = :post
form_options[:multipart] = true
options[:timestamp] = Time.now.to_i
options[:callback] = callback_url
if options[:transformation]
options[:transformation] = Cloudinary::Utils.
generate_transformation_string(options[:transformation])
end
options[:signature] = Cloudinary::Utils.
api_sign_request(options, Cloudinary.config.api_secret)
options[:api_key] = Cloudinary.config.api_key
url = "https://api.cloudinary.com/v1_1/"
url<< "#{Cloudinary.config.cloud_name}/image/upload"
form_tag(url, form_options) do
content = []
options.each do |name, value|
content<< hidden_field_tag(name, value)
end
content<< capture(&block)
content.join("\n").html_safe
end
end
Its just a wrapper around form_tag that adds few hidden fields and calculate request signature.
The usage is really simple:
<%= cl_form_tag CALLBACK_URL_HERE,
form: { class: 'cloudinary' },
format: 'jpg',
transformation: { size: '300x300', crop: 'fit' } do %>
<%= file_field_tag :file %>
<%= submit_tag "Upload" %>
<% end %>
When user picks file and clicks Upload button, file will start uploading to Cloudinary (completely bypassing your server). Once the upload process is complete, you’ll be redirected back to specified callback url.
cl_form_tag is calculating signature and including it into hidden field. It calculates signature based on your Cloudinary.config.api_scret option. The same thing happens on Cloudinary server when it responds. Thats why you always need to validate response signature to make sure that the Cloudinary itself made that request to callback url. Here is simple method that can do this check:
def valid_cloudinary_response?
received_signature = request.query_parameters[:signature]
calculated_signature = Cloudinary::Utils.api_sign_request \
request.query_parameters.except(:signature),
Cloudinary.config.api_secret
received_signature == calculated_signature
end
That’s it! Pretty simple but powerful. There is bunch more that Cloudinary can do, so check the documentation for more details.