Direct Image Uploading on Heroku

by Leo Royzengurt

August 26, 2016

Efficient client side image uploading can sometimes be a daunting task, especially if the web application you are working on has thousands of users uploading simultaneously. While Rails has many gems that are quite easy to set up, most don’t perform well under consistent heavy usage or when each user needs to upload multiple images at a time.

We will go over two different approaches to do this more efficiently; easing the strain on your servers and dramatically improving your user’s experience.

Option 1: Direct single image upload through Cloudinary with Javascript.

To begin this process, you will need to install the Cloudinary gem. You can do this by simply adding the following to your gemfile. As well you will need to add the cloudinary addon via heroku.

gem 'cloudinary'

The next step is to download your cloudinary.yml file from your Cloudinary account and place it into your config folder. It will look similar to this

cloud_name: "example"
api_key: "example_api_key"
api_secret: "example_api_secret"

Now in order to do uploads with jquery you will need to download the following jquery plugins


After they are downloaded, I would advise putting them in your vendor assets folder. Once this is done we will place an input in the form:

data-form-data=" ... html-escaped JSON data ..."

You can set the name of the file to your model name and attribute so it persists into the controller as a param hash, or you can have a hidden input with the param name. After the file is uploaded it will return JSON data so that we can then grab the image's URL and put it into the hidden field.

$('.cloudinary-fileupload').bind('cloudinarydone', function(e, data) {

Option 2: Uploading multiple images with jQuery File Upload and then losslessly compressing them with tinypng.

First start off by downloading jquery-fileupload.js and placing it into your vendor assets folder. Then install the Tinypng gem by placing the following in your gem file.

gem 'tinify'

Next, create a form with multiple file inputs or create a nested attributes form. Check out our previous blog post for a more in-depth walk through of nested attributes. Once that is done, we will have an input that we will use in a variable. For now, we will assume the file upload has a class of ‘upload’.

$("#yourform").on('click', '.upload input:file', function( evt ) {
var $fileInput = $( this );
fileInput: $fileInput,
url: "/images/image_uploader",
type: 'POST',
autoUpload: true,
disableImageResize: false,
imageMaxWidth: 1200,
imageMaxHeight: 1200,
imageOrientation: false,
loadImageMaxFileSize: 1000000000,
prependFiles: true,
paramName: 'file',
dataType: 'json',
replaceFileInput: false,
change: function (e, data) {
// here you can execute code on each change
start: function( e ) {
// here you can execute code on the start of the upload
progress: function( e, data ) {
// here you can execute code that happens on the progress of the image upload below is a sample on how to get a percent of the code
var percent = Math.round( (e.loaded / * 100 );
progressall: function( e, data ) {
// here is code based on the progress of all the uploads
done: function(e, data) {
var image_url = data.result.url
// once the image is done we can obtain the returned value of the url with the code above
fail: function(e, data) {

For the above code to work you will need to have an ajax POST route set up in your applications. In your controller add the following:

def image_uploader
Tinify.key = "yourtinifykey"
#grab the file from the templfile that you send over
source = Tinify.from_file(params[:file].tempfile)
@new_id = SecureRandom.uuid
#the reason we use a SecureRandom.uuid is so that there is never an overwrite if the filenames are similar
#next we store the upload directly to s3
service: "s3",
aws_access_key_id: "yourawsaccesskey",
aws_secret_access_key: "yourawssecretkey",
region: "yourregion",
path: "path/path/#{@new_id}/#{@new_id}.jpg"
remote_url = "{@new_id}/#{@new_id}.jpg"
render :json => {:url => remote_url }.to_json

After this is done you will have an image URL returned that you can now plug into a hidden file input field and will be saved along with the rest of the data on your form.

There will most likely be other use cases in your app since no app is the same so feel free to message me at if you have any questions or just want to chat about development.