Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

react-s3-uploader

odysseyscience183kMIT5.0.0TypeScript support: included

React component that renders a file input and automatically uploads to an S3 bucket

react, upload, component, s3, bucket

readme

react-s3-uploader

Provides a React component that automatically uploads to an S3 Bucket.

Install

$ npm install --save react-s3-uploader

From Browser

var ReactS3Uploader = require('react-s3-uploader');

...

<ReactS3Uploader
    signingUrl="/s3/sign"
    signingUrlMethod="GET"
    accept="image/*"
    s3path="/uploads/"
    preprocess={this.onUploadStart}
    onSignedUrl={this.onSignedUrl}
    onProgress={this.onUploadProgress}
    onError={this.onUploadError}
    onFinish={this.onUploadFinish}
    signingUrlHeaders={{ additional: headers }}
    signingUrlQueryParams={{ additional: query-params }}
    signingUrlWithCredentials={ true }      // in case when need to pass authentication credentials via CORS
    uploadRequestHeaders={{ 'x-amz-acl': 'public-read' }}  // this is the default
    contentDisposition="auto"
    scrubFilename={(filename) => filename.replace(/[^\w\d_\-.]+/ig, '')}
    server="http://cross-origin-server.com"
    inputRef={cmp => this.uploadInput = cmp}
    autoUpload={true}
    />

The above example shows all supported props.

This expects a request to /s3/sign to return JSON with a signedUrl property that can be used to PUT the file in S3.

contentDisposition is optional and can be one of inline, attachment or auto. If given, the Content-Disposition header will be set accordingly with the file's original filename. If it is auto, the disposition type will be set to inline for images and attachment for all other files.

server is optional and can be used to specify the location of the server which is running the ReactS3Uploader server component if it is not the same as the one from which the client is served.

Use scrubFilename to provide custom filename scrubbing before uploading. Prior to version 4.0, this library used unorm and latinize to filter out characters. Since 4.0, we simply remove all characters that are not alphanumeric, underscores, dashes, or periods.

The resulting DOM is essentially:

<input type="file" onChange={this.uploadFile} />

The preprocess(file, next) prop provides an opportunity to do something before the file upload begins, modify the file (scaling the image for example), or abort the upload by not calling next(file).

When a file is chosen, it will immediately be uploaded to S3 (unless autoUpload is false). You can listen for progress (and create a status bar, for example) by providing an onProgress function to the component.

Extra props

You can pass any extra props to <ReactS3Uploader /> and these will be passed down to the final <input />. which means that if you give the ReactS3Uploader a className or a name prop the input will have those as well.

Using custom function to get signedUrl

It is possible to use a custom function to provide signedUrl directly to s3uploader by adding getSignedUrl prop. The function you provide should take file and callback arguments. Callback should be called with an object containing signedUrl key.

import ApiClient from './ApiClient';

function getSignedUrl(file, callback) {
  const client = new ApiClient();
  const params = {
    objectName: file.name,
    contentType: file.type
  };

  client.get('/my/signing/server', { params })
  .then(data => {
    callback(data);
  })
  .catch(error => {
    console.error(error);
  });
}


<ReactS3Uploader
  className={uploaderClassName}
  getSignedUrl={getSignedUrl}
  accept="image/*"
  onProgress={onProgress}
  onError={onError}
  onFinish={onFinish}
  uploadRequestHeaders={{
    'x-amz-acl': 'public-read'
  }}
  contentDisposition="auto"
/>

Server-Side

Bundled router

You can use the Express router that is bundled with this module to answer calls to /s3/sign

app.use('/s3', require('react-s3-uploader/s3router')({
    bucket: "MyS3Bucket",
    region: 'us-east-1', //optional
    signatureVersion: 'v4', //optional (use for some amazon regions: frankfurt and others)
    signatureExpires: 60, //optional, number of seconds the upload signed URL should be valid for (defaults to 60)
    headers: {'Access-Control-Allow-Origin': '*'}, // optional
    ACL: 'private', // this is default
    uniquePrefix: true // (4.0.2 and above) default is true, setting the attribute to false preserves the original filename in S3
}));

This also provides another endpoint: GET /s3/img/(.*) and GET /s3/uploads/(.*). This will create a temporary URL that provides access to the uploaded file (which are uploaded privately by default). The request is then redirected to the URL, so that the image is served to the client.

If you need to use pass more than region and signatureVersion to S3 instead use the getS3 param. getS3 accepts a function that returns a new AWS.S3 instance. This is also useful if you want to mock S3 for testing purposes.

To use this you will need to include the express module in your package.json dependencies.

Access/Secret Keys

The aws-sdk must be configured with your account's Access Key and Secret Access Key. There are a number of ways to provide these, but setting up environment variables is the quickest. You just have to configure environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, and AWS automatically picks them up.

Other Types of Servers

Boto for Python, in a Django project
import boto
import mimetypes
import json

...

conn = boto.connect_s3('AWS_KEY', 'AWS_SECRET')

def sign_s3_upload(request):
    object_name = request.GET['objectName']
    content_type = mimetypes.guess_type(object_name)[0]

    signed_url = conn.generate_url(
        300,
        "PUT",
        'BUCKET_NAME',
        'FOLDER_NAME' + object_name,
        headers = {'Content-Type': content_type, 'x-amz-acl':'public-read'})

    return HttpResponse(json.dumps({'signedUrl': signed_url}))

Ruby on Rails, assuming FOG usage

# Usual fog config, set as an initializer
storage = Fog::Storage.new(
  provider: 'AWS',
  aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
  aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)

# In the controller
options = {path_style: true}
headers = {"Content-Type" => params[:contentType], "x-amz-acl" => "public-read"}

url = storage.put_object_url(ENV['S3_BUCKET_NAME'], "user_uploads/#{params[:objectName]}", 15.minutes.from_now.to_time.to_i, headers, options)

respond_to do |format|
  format.json { render json: {signedUrl: url} }
end

Micro

const aws = require('aws-sdk')
const uuidv4 = require('uuid/v4')
const { createError } = require('micro')

const options = {
  bucket: 'S3_BUCKET_NAME',
  region: 'S3_REGION',
  signatureVersion: 'v4',
  ACL: 'public-read'
}

const s3 = new aws.S3(options)

module.exports = (req, res) => {
  const originalFilename = req.query.objectName

  // custom filename using random uuid + file extension
  const fileExtension = originalFilename.split('.').pop()
  const filename = `${uuidv4()}.${fileExtension}`

  const params = {
    Bucket: options.bucket,
    Key: filename,
    Expires: 60,
    ContentType: req.query.contentType,
    ACL: options.ACL
  }

  const signedUrl = s3.getSignedUrl('putObject', params)

  if (signedUrl) {
    // you may also simply return the signed url, i.e. `return { signedUrl }`
    return {
      signedUrl,
      filename,
      originalFilename,
      publicUrl: signedUrl.split('?').shift()
    }
  } else {
    throw createError(500, 'Cannot create S3 signed URL')
  }
}
Other Servers

If you do some work on another server, and would love to contribute documentation, please send us a PR!

changelog

5.0.0
  • Update several dependencies, moving some to optional and dev
  • Update many typescript definitions
  • Added mime-types fallback when browser doesn't provide file.type [#223]
4.9.3
  • Add missing typescript definitions [#216]
4.9.2
  • Added typescript definitions
4.9.0
  • Better error messaging [#207]
  • Configurable Expires time [#208]
4.8.0
  • Allow 201 response code from PUT [#174]
4.7.0
  • Add an onSignedUrl lifecycle hook [#170]
4.6.2
  • Fix undefined in file path when not providing s3path prop [#160]
4.6.1
  • Fix s3path and uniquePrefix ordering when applied together [#156]
4.6.0
  • Add autoUpload prop, which can be set to false to disable automatic upload [#95] [#107] [#155]
4.5.1
  • Add inputRef prop [#153]
4.5.0
  • Removed peerDependencies on react and react-dom [#136]
4.4.0
  • Support getS3 function in bundled router options [#139]
  • Support s3Path string prefix prop [#140]
4.3.0
  • Support middleware in express module with optional second arg
  • Allow 200 or 201 for success on sign request
4.2.0
  • Switch to uuid instead of node-uuid [#115]
  • Not using React.DOM.input [#127]
  • Allow function for signingUrlHeaders
  • Support setting headers in signResult callback
4.1.1
  • Fix INVALID_STATE_ERR on Safari 5 [#118]
4.1.0
  • Using create-react-class and prop-types to fix deprecation warnings [#116]
4.0.3
  • Return fileKey in response bundled router sign response
4.0.2
  • Add uniquePrefix option to express router support turning off the UUID prefix of filenames. Default is true, but set to false to preserve original filenames.
4.0.1
  • Don't pass scrubFilename prop to <input>
4.0.0
  • BREAKING CHANGE: Removed unorm and latinize dependencies, which were used to scrub file names before uploading. Now we just remove all characters that are not alphanumeric, underscores, dashes, or periods. If you need different behavior, please provide a custom scrubFilename function in props.
3.4.0
  • Adding optional prop signingUrlMethod (default: GET) [#103]
  • Adding optional prop signingUrlWithCredentials [#103]
  • Adding abort to react component [#96]
3.3.0
  • Adding optional preprocess hook supports asynchronous operations such as resizing an image before upload [#79 #72]
  • Fix uglify warning [#77]
3.2.1
  • Avoid react warning by not passing unnecessary props to Dom.input [#75]
3.2.0
  • Allow custom getSignedUrl() function to be provided [#22]
3.1.0
  • Replace unsafe characters (per AWS docs) with underscores [#69]
3.0.3
  • Support signatureVersion option
3.0.2
  • Not passing non-JSON response text to error handlers
3.0.1
  • Fixes issue where URL would include "undefined" if this.server was not specified
3.0
  • Using react-dom
2.0.1
  • Fixes issue where URL would include "undefined" if this.server was not specified
2.0
  • Breaking Change [Fixes #52] Removing express as a peerDependency. Projects should explicitly depend on express to use the bundled router
  • [Fixes #51] url encode the contentType
1.2.3
  • Fixes issue where URL would include "undefined" if this.server was not specified
1.2.2
  • [Fixes #48] Only setting the AWS region for the S3 client, not the global default
1.2.1
  • Added server prop to ReactS3Uploader to support running the signing server on a different domain
  • Added headers option to s3router to support specifying 'Access-Control-Allow-Origin' header (or any others)
  • [Fixes #44] Using unorm.nfc(str) in favor of str.normalize()
1.2.0
  • Added dependencies unorm and latinize for uploading files with non-latin characters.
  • Filenames are normalized, latinized, and whitespace is stripped before uploading