Skip to content

Instantly share code, notes, and snippets.

@peter
Last active November 29, 2017 14:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peter/84750f3543a5abbe9157ebbe48d9faf5 to your computer and use it in GitHub Desktop.
Save peter/84750f3543a5abbe9157ebbe48d9faf5 to your computer and use it in GitHub Desktop.
S3 Direct Upload from JavaScript with the aws-sdk package

S3 Direct JavaScript Upload

TypeScript Code

S3 client module:

import * as AWS from 'aws-sdk'

export const REGION = 'eu-west-1'

export const TMP_FILES_CREDENTIALS = {
  AWS_ACCESS_KEY_ID: '...',
  AWS_SECRET_ACCESS_KEY: '...'
}

export const TMP_FILES_BUCKET = 'tmp-files'

export function configure(credentials: object) {
  AWS.config.update({
    region: REGION,
    credentials: new AWS.Credentials(credentials['AWS_ACCESS_KEY_ID'], credentials['AWS_SECRET_ACCESS_KEY'])
  })
}

export function upload(bucket: string, key: string, body: any, contentType: string) {
  const s3 = new AWS.S3({params: {Bucket: bucket}})
  const params = {Bucket: bucket, Key: key, ContentType: contentType, Body: body, ACL: 'public-read'}
  // Documentation: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
  return s3.upload(params)
}

Test:

import client from './s3.upload.client'
import { assertEqual } from '../shared/spec-helper'
import HttpClient from '../shared/http-client'

describe('s3.upload.client', () => {
  describe('upload', () => {
    it('can upload a file to S3', (done) => {
      client.configure(client.TMP_FILES_CREDENTIALS)
      const bucket = client.TMP_FILES_BUCKET
      const key = 'a2e2ad5b798a4242a4cac6367ba3a9c0.xml'
      const data = '<s3-upload-client-test/>'
      const contentType = 'application/xml'
      client.upload(bucket, key, data, contentType)
        .promise().then((result: any) => {
          // console.log('s3.upload.client upload complete')
          assertEqual(result['Key'], key)
          assertEqual(result['Bucket'], bucket)
          const url = result['Location']
          HttpClient.get(url).then((result: object) => {
            // console.log('s3.upload.client fetched S3 file from public URL', url)
            assertEqual(result['data'], data)
            done()
          })
        }, (error: any) => {
          console.log('s3.upload.client error', error)
          assertEqual(false, true)
        })
    })
  })
})

AWS Console Configuration

A dedicated Tmp file upload user belonging to a "TmpFile" IAM Group with a policy that allows access to a specific tmp-files bucket:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:GetObjectTagging",
                "s3:GetObjectTorrent",
                "s3:GetObjectVersion",
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionTagging",
                "s3:GetObjectVersionTorrent",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:PutObjectTagging",
                "s3:PutObjectVersionAcl",
                "s3:PutObjectVersionTagging",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::tmp-files/*"
            ]
        }
    ]
}

Bucket CORS configuration:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
    <ExposeHeader>ETag</ExposeHeader>
</CORSRule>
</CORSConfiguration>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment