Search code examples
javascriptcanvasember.jsamazon-s3fileapi

Upload image by URL in single-page application with Canvas and File API


We have a single-page application (Rails back-end, Ember.js front-end) where we're currently moving away from a server-side image uploader to a client-side image uploader. We previously used the Carrierwave gem to do resizing and sending to S3 on the server. Now we want to the resizing (using HTML5 Canvas and File API) and sending to S3 directly on the client.

This works well when the user selects an image from his computer. It's definitely way faster for the user and puts less load on the server.

However, our users have gotten used to the "Upload by URL" functionality that we offered. It works like the "Search by image" functionality on Google Image Search. Instead of selecting a file from his computer, the user pastes a URL to an image.

Because of the same-origin policy, we cannot use Canvas API on an external image (it becomes tainted, see https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Using_images#Using_images_from_other_domains).

I see two possible solutions:

  1. In order to be able to upload to S3 directly from the client, we need to generate a pre-signed key on the server. We could pass the URL to the image in that same request, download it on the server while we generate the pre-signed key, and put the image as base64 payload in the response.
  2. Use a proxy on our domain and use it to bypass the SOP. So access the image on the client as https://www.mydomain.com/?link=http://www.link.to/image/selected/by/user.jpg.

My questions are:

  1. Do you know any other way to bypass the same-origin policy to provide a "Upload by URL" functionality?
  2. Which solution do you think is best?
  3. How hard is it to setup 2)? I have no experience in setting up proxies. FWIW, we host our application on Heroku.

I hope the situation I described is clear enough.

Thank you!

Yoran


Solution

    1. Yes, you could force your clients to download the other-domain image to their local drives and then upload that copy from their local drives.

    2. "Best" is subjective & relative to your configuration. The traditional workaround is your option#2--bounce the image off your server. Really all you're doing is having your server upload the image and re-serve it to the client. If you're expecting a huge volume of images, then forcing the client to download their own images might be better rather than gumming up your server by "cleaning" their images.

    3. How hard to set up? It's fairly easy...after all you're just having some server code pull a remote image and save it to a specified server directory. The only modestly hard part is:

      • Make sure the server never interprets one of those client-inspired urls as executable (viruses!)

      • Clear the new directory often so the server is not over-weighted with images loaded for the client

      • Set limits on the size and quantity of images the client can upload to your server (denial-of-service attack!).