Skip to content

Upload to Amazon S3 using HTML5 runtime

jbourassa edited this page Apr 14, 2013 · 6 revisions

Being able to upload files to Amazon S3, especially in HTML5, has been a goal for quite some time and while it was somehow possible in Flash and Silverlight, HTML5 was out of the game. Amazon S3 simply refused to add support for Access-Control-Allow-Origin - that single miraculous header that helps cross-domain AJAX requests to reach the server. Finally, after continuous lament from users, with bleeding shouts like: "Two and a half year later, still no cigar... AWS, Y U MAKIN ME CRY? :'(", Amazon made it happen.

So now it's possible. And here we will tell you how.

Disclammers

  • This is a working draft;
  • Silverlight has not been tested. This article focuses on HTML5 and Flash runtimes;
  • There are several workarounds for this method to work. I'm hoping we can work together to improve plupload to make this process easier.
  • This guide assumes intermediate-to-advanced overall knowledge. Don't expect to copy-paste the examples and things to just work.

1. CORS & crossdomain.xml

In order for this to work, CORS configs and crossdomain.xml are required.

HTML : Edit the CORS cofig in your S3 Console (guide) with something like :

<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedHeader>*</AllowedHeader>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
    </CORSRule>
</CORSConfiguration>

Flash : Create a public file named crossdomain.xml at the root of your bucket with this content :

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM
"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="*" secure="false" />
</cross-domain-policy>

2. Generating a policy and a signature

Uploading to S3 with multipart requires a policy and a signature. See this article for detailed information. These two will be passed to Plupload as multipart params (more on this later).

Heres a working policy generator written in Ruby. The code is intentionnaly simple and uses globals, please do not blindly copy-paste) :

require 'base64'
require 'json'
require 'digest/sha1'

$ACL = 'public-read' # Change this according to your needs
$BUCKET = 'YOUR_BUCKET'
$AWS_SECRET = 'YOUR_SECRET'

def policy
  conditions = [
    ["starts-with", "$utf8", ""],
    # Change this path if you need, but adjust the javascript config
    ["starts-with", "$key", "uploads"],
    ["starts-with", "$filename", ""],
    { "bucket" => $BUCKET },
    { "acl" => $ACL }
  ]

  policy = {
    # Valid for 3 hours. Change according to your needs
    'expiration' => (Time.now.utc + 3600 * 3).iso8601,
    'conditions' => conditions
  }

  Base64.encode64(JSON.dump(policy)).gsub("\n","")
end

def signature
  Base64.encode64(
    OpenSSL::HMAC.digest(
      OpenSSL::Digest::Digest.new('sha1'),
      $AWS_SECRET, policy
    )
  ).gsub("\n","")
end

3. Configuring Plupload

{
  // General settings
  runtimes : 'flash,html5',

  // Flash settings
  flash_swf_url : '/plupload/src/moxie/bin/flash/Moxie.swf',

  // S3 specific settings
  url : "https://<%= $BUCKET %>.s3.amazonaws.com:443/",
  file_name_name: false, // Custom option to our fork to remove file_name_name
  multipart: true,
  multipart_params : {
    // Dummy filename to ensure the field is sent in HTML just like Flash
    // This allow to have a consistent AWS policy for both HTML and Flash
    filename: 'filename',
    utf8: true,

    AWSAccessKeyId: "YOUR_AWS_ACCESS_KEY_ID",
    acl: "public-read", // See http://docs.aws.amazon.com/AmazonS3/latest/dev/ACLOverview.html#CannedACL
    // See Generating a policy and a signature
    policy: "YOUR_POLICY",
    signature: "YOUR_SIGNATURE",

    // This is basically the resulting location of the file on S3.
    // See http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPOST.html#RESTObjectPOST-requests-form-fields
    //
    // WARNING : Change this to suite your needs.
    // You need to insert some sort of unique identifier to avoid
    // overriding files if they share the same filename.
    key: "uploads/${filename}",
  }
}

4. Putting it all together

I made a little Sinatra one-file-app that shows how to use all of this together.

Caveats

  • S3's success_action_redirect does not work in Chrome. I think the problem is that Moxie's XHR does not handle well redirect codes, but investigation is needed.
  • This demo requires a patched version of Plupload. Pull request #750 has to be merged for this to work with master.

Refs

Clone this wiki locally