Imagine you are riding your bicycle. The road ahead is clear and the sky bright and blue. You start thinking about the view you will have from the top of the mountain you are closing in on. You start climbing and your breath quickens. It's time to adjust gears. You come to an halt and jump off the bike. Kneeling on it's side you grab the chain and move it to a better suited cog and off you go again.
Nobody would like to ride a bike without an shifting system in the mountains. Why do you use Ruby file upload gems where you have to do this exact same thing when it comes to different versions of images?
What Is Wrong with Most Ruby File Upload Solutions?
It is common to display uploaded images in various sizes throughout a Ruby web application. Let's call these various sizes versions for the time being.
Popular file upload gems like
Paperclip allow you to define these different versions in your application code. The definitions allow the gem to process and generate the versions upfront whenever a new image gets uploaded.
This upfront image processing is the problem.
Why Is Upfront Image Processing Wrong?
It complicates and slows down the development process. Giving every version a specific name is the classic naming things problem we all know. Should we call it
profile_picture? And what happens if the image size requirements change for a specific part of the application? Do we invent another version with a new name? Or does this requirement change in one part of the application effect other parts? If you care about optimal images sizes and have multiple use cases with different sizes you will end up in a version naming mess.
Adjusting the versions itself is paired with additional work. Since all versions are generated upfront at the time of the upload you have to re-generate them whenever you change a version that is already in use. This is also the case whenever you introduce a new version into your system. Unfortunately this is a characteristic of upfront image processing. It discourages you and your team from adjusting and testing different image sizes.
Doing upfront image processing also adds a performance penalty to your pageloads. For every uploaded image all versions need to be created before the response can be sent to the client. Doing such heavy work in the request lifecycle is bad practice therefore you are almost forced to offload the image processing into background jobs. For some inexplicable reason none of the popular gems provides a background adapter out of the box.
How Does on the Fly Image Processing Work?
Instead of generating specific versions upfront, the correct image is generated whenever a client requests it. In practice the image URL usually contains parameters that specify the exact size of the image. Whenever such an image is requested the application extracts these parameters and generates a new image based on the original.
To improve performance the newly generated image can be stored or cached. The next time someone requests the image with the same size the webserver can respond without having to forward the request to the application.
Embedding the image size information in the URL allows you to experiment with it. Changing image sizes and experimenting with them doesn't mean additional work. The system generates new sizes as there is need.
Which Ruby File Upload Gems Offer on the Fly Image Processing?
The most established gem is probably Dragonfly by Mark Evans. It allows you to handle uploads in any Rack based Ruby application and can process files on the fly. Dragonfly is also able to process non-image files and supports user defined processes.
Refile is the successor of CarrierWave and Jonas Nicklas third attempt at getting file uploads right. While it's still a pretty new gem it has nice features like AJAX file uploads out of the box.
Speaking of new there is also a third contender, the MountableFileServer gem. Inspired by Dragonfly and Refile this is my attempt at building a simple file upload solution that supports on the fly image processing. Although there is no official release yet, it is already used in production and lets us build sophisticated interfaces that deal with file uploads.