Uploading Files to Amazon S3 Directly from a Browser using JavaScript

It’s common in many webapps to let the user upload files to your system. These could be as simple as profile pictures or something else. In AWS, the ideal place to keep these files is S3 but if you’re uploading the files to your server & then to S3, you don’t have to. You can upload to S3 directly from the browser. Let’s see how.

What you want is browser-based uploads to S3 using HTTP POST calls to S3 APIs from JavaScript. This works by generating “signed policies” on the server & sending them to the browser. Once client-side code has the signed policy, it can upload using POST directly to S3 without going through the server again.

  1. The user opens a web browser and accesses your web page.
  2. Your web page contains an HTTP form that contains all the information necessary for the user to upload content to Amazon S3.
  3. The user uploads content directly to Amazon S3.

See Authenticating Requests in Browser-Based Uploads Using POST (AWS Signature Version 4).

The browser ends up with a page containing a form with all the fields required for a direct upload to S3:

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <form action="http://my-bucket.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
    Key to upload: 
    <input type="input"  name="key" value="user/user1/${filename}" /><br />
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="success_action_redirect" value="http://my-bucket.s3.amazonaws.com/successful_upload.html" />
    <input type="input"  name="Content-Type" value="image/jpeg" /><br />
    <input type="hidden" name="x-amz-meta-uuid" value="14365123651274" /> 
    <input type="hidden" name="x-amz-server-side-encryption" value="AES256" /> 
    <input type="text"   name="X-Amz-Credential" value="AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request" />
    <input type="text"   name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
    <input type="text"   name="X-Amz-Date" value="20151229T000000Z" />
    Tags for File: 
    <input type="input"  name="x-amz-meta-tag" value="" /><br />
    <input type="hidden" name="Policy" value='<Base64-encoded policy string>' />
    <input type="hidden" name="X-Amz-Signature" value="<signature-value>" />
    <input type="file"   name="file" /> <br />
    <!-- The elements after this will be ignored -->
    <input type="submit" name="submit" value="Upload to Amazon S3" />

See Browser-Based Upload using HTTP POST (Using AWS Signature Version 4).

Notice the form action is sending the file directly to S3, not to your server. You’ll typically make the policy expire after a few minutes so the page must be refreshed before uploading. This lets you monitor & limit uploads. The only thing going to or from your server are the signed URLs. Your secret keys stay secret on the server.

Another way of handling this entire browser-based uploads if you’re using Cognito in your application, is to use Cognito identity pools & have Cognito grant the permissions required to upload to S3 directly. See Uploading Photos to Amazon S3 from a Browser.