Filepicker and Aviary - Image uploading on steroids

We all have been using the same code for uploading images for years, but didn’t
you always feel that there is something wrong with it? For every other task
like writing texts, picking a date, selecting from lot of choices we have a
good tool that can help in implementing such feature and improve the user
experience, but file uploads almost always feel a little broken. There are some
Flash tools that might help, but they are still not good enough.

Solution

Well, welcome to the world invaded by Filepicker
and Aviary. Speaking short, Filepicker is a tool that
let the user upload images not only from the computer itself but also from web
services such as Facebook or Dropbox. Aviary provides you with a powerful HTML5
editor for manipulating photos. Both of them process the images on their
servers and provide you an url for downloading a file. If you only need more
powerful uploading you can stick with
Filepicker widgets
otherwise we need to get our hands dirty with their Javascript APIs
(or CoffeeScript as you will see) but is not hard at all.

Working with code

Let’s start with the view:

<%= link_to _("Set avatar"), "#", :'data-avatar' => "set" %%>

<ahref="#"data-avatar="set">Set avatar</a>

Nothing fancy here. Classic Rails link_to method, using _('') method for
translating with FastGettext. We
don’t care about URL because we are going to handle clicks in Javascript so I
used "#" as URL. Instead of using css classes or id for such link
I prefer to use custom data-* attribute

First, we need to display Filepicker popup for choosing image when our link is
clicked.

When user finishes editing the photo and presses “Save” button, onSave
callback is executed. You can save the url value in JS variable or use it to
fill some hidden field in a form or send it to the server. However the
documentation states that “this image may not yet be ready so you will have
to poll this link, or alternatively handle the hi-res image server-side”.
This is my biggest disappointment when using those two products. For that
reason we are going to use postUrl option so that Aviary will send us a
request to this given URL when the image is ready. Obviously you will have
to use different value of the setting for development, staging and production
environment. In development you can either forward some port from your router
(I assume it is publicly available) to your computer or alternatively, if
have a server you can use ssh to forward traffic from the server to your
local machine.

Find user, set avatar url and save. As simple as that. Where does
remote_avatar_url setter comes from ? It is a feature of
carrierwave library that I use
to store and resize avatars. It can download the remote avatar itself so I
do not need to bother myself with that. You can use it with
RMagick,
mini_magick
or vips.

classUser<ActiveRecord::Basemount_uploader:avatar,AvatarUploaderend

classAvatarUploader<CarrierWave::Uploader::BaseincludeCarrierWave::RMagick# include CarrierWave::MiniMagick# include CarrierWave::VipsincludeSprockets::Helpers::RailsHelperincludeSprockets::Helpers::IsolatedHelperstorage:filedefstore_dir"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"enddefdefault_urlasset_path"avatar/default.png"endversion:largedoprocess:resize_to_fit=>[256,256]endversion:mediumdoprocess:resize_to_fit=>[128,128]endversion:smalldoprocess:resize_to_fit=>[64,64]endend

But we don’t want anyone to be capable to send requests to our application
and change avatars, do we ? We need to add some protection. And we need to know
which user avatar should be changed. Every user will have its own token for
updating the avatar. Again, we use custom data-* (exactly
data-avatar-token) attribute to store the token in HTML.

What do you compute digest of ? Well, that depends on your application.

One more thing that I would like to do is to always get square images
from Aviary. I could not find 100% reliable way of doing that. My trick
is to allow users to use only one type of crop ratio and show the crop
tool as initial one. However, the user can still press
“Cancel” unfortunately.

The concepts behind Filepicker and Aviary are amazing and I believe they will change the web. It’s like ‘Editor as a Service’, 'Picker as a Service’. What else could be a service ? I would love to use Gliffy editor in my app the same way I use Aviary.

Filepicker can store files directly in S3 so you do not have to keep them. I just prefer to have them on my machine.

Javascripts are available via HTTPS links.

Cons:

When using filepicker the user accepts filepicker.io application when connecting to Facebook or Dropbox, not our own application. This might be also considered a good thing if you did not connected your App with Facebook, but I would prefer if the widgets asks for permissions for my app. However I am not sure if that would be possible at all.

Both services ask you to link directly to their Javascript files instead of downloading them and using in your asset pipeline solution. So there are going to be additional HTTP requests when loading the page. But the good side is that if they fix some bug or improve the editor, the changes will be automatically available to your users with you deploying your app again.

After save, the photo URL from Aviary is not available immediately. This presents a huge UI problem. What should I show to my user after setting new avatar when I might not yet have a new avatar image to display ? Even after refresh of the page the new avatar might not yet be ready if the server is still waiting for a request from Aviary.

Aviary is not doing exponential backoff. It sends the request to your server only once. The game is over if you failed to handle it. (Sidenote: if you ever need to implement exponential backoff strategy in Ruby or Rails, check exponential-backoff gem.

The full list of Aviary translations is not bad but it is still missing few important ones for me like Greek or Turkish (forgive me my Eurocentrism).

You cannot change the language after initial Aviary configuration. Single Page Applications that are capable of changing language without reloading the page probably need to create a new instance every time they want to use Aviary editor instead of calling launch method multiple times on one object.