-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Upload to Amazon S3
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 send Access-Control-Allow-Origin
header - that single miraculous one that makes AJAX requests to suddenly reach the server across domains. Finally, after continuous lament from users, with bleeding shouts like: "Two and a half year later, still no cigar?.." - Amazon made it happen.
So now it's possible.
- Disclaimer
- Preface
-
Prepare server-side (S3)
- ... for HTML5 runtime
- ... for Flash runtime
- ... for Silverlight runtime
- We assume that you already have active S3 account.
- We describe the most generic scenario. Feel free to customize it to your needs.
- This is a working draft. Suggestions are welcome.
Do not expect it to just work. It is definitely achievable, but still requires some effort (not that much though). Each runtime has it's own specifics and requirements. Flash and Silverlight are similar in some sense and in general can share exactly the same configuration, both server- and client-side. But again - there's an option.
## Prepare server-side (S3)First, you need to create a bucket and make it accessible. We simply grant Upload/Delete
permissions to Everyone
.
And done.
Next step.
### ... for HTML5 runtimeIn the same Permissions
section there is an option to Add CORS Configuration
(if you do not know what the CORS is - HTML5 Rocks has an excellent write-up about it):
As always there are some options, but we will use the most generic configuration to make sure that S3 bucket is indeed compatible with our HTML5 upload:
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedHeader>*</AllowedHeader>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
</CORSRule>
</CORSConfiguration>
What we say here is that we allow cross-domain access from any domain, using requests with any headers, via GET or POST. Preflight requests will be cached for 3000 secs.
### ... for Flash runtimeTo support cross-origin requests, Flash requires crossdomain.xml
- special policy file at the root of your bucket with such content for example:
<?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>
Again, we simply allow all domains here. You might want to restrict this to specific ones only. Also notice an attribute secure="false"
- it will make your bucket accessible via HTTPS. If you want to allow only secured connections, then set this attribute to true
.
Finally, do not forget to make crossdomain.xml
public - files do not automatically inherit bucket permissions and you have to set them manually.
In fact you can stop here and do nothing else for Silverlight.
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
{
// 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}",
}
}
I made a little Sinatra one-file-app that shows how to use all of this together.
- 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.