useUpload

A react hook to upload files to any cloud service using presigned urls gotten through a REST or Graphql API

github.com/ernestognw/use-upload

Getting started

Installation

With yarn:

yarn add use-upload

With npm:

npm install --save use-upload

Usage

You will need an API endpoint that returns you a presigned URL from the storage service you are using. If you don't know what a presigned URL is, please go to the 'How to use' section down below and you'll learn more

Once you have a valid endpoint, just import accordingly:

import { useRestUpload } from 'use-upload'

useUpload hook returns an object with 4 attributes, described as follows:

  • upload: () => String - Function to upload the file. It returns a string containing the final URL

  • uploading: Boolean - Flag to show whether the file is being uploaded or not

  • progress: Number - Progress of uploading from 0 to 100

  • reset: () => void - Function to reset the progress

You can use them this way:

const MyComponent = () => {
  const { 
    upload,
    uploading,
    progress,
    reset 
  } = useRestUpload(
    '<YOUR-ENDPOINT>', 
    { ...config }
  )

  const handleUpload = () => {
    // Config is equivalent to client.query(_, config)
    // See: https://github.com/axios/axios#request-config
    const finalUrl = await upload(file, config);
  }

  return (
    ...
  )
}

For more info you can refer to the 'API Reference' section

Examples

Working example:


0%

Code:

import React, { useState } from 'react';
import { useRestUpload } from 'use-upload';

const Rest = () => {
  const [url, setUrl] = useState('');
  const [message, setMessage] = useState('');
  const { upload, progress, uploading, reset } = useRestUpload(
    'http://use-upload.free.beeceptor.com/rest/signFileUrl'
  );

  const onFileChange = async (event) => {
    const [file] = event.target.files;
    if (file.size > 1000000) {
      setMessage('Sorry, but beeceptor API mock only works with files under 1 MB');
      return;
    }
    const fileUrl = await upload(file, { body: { filePath: file.name, fileType: file.type } });
    setUrl(fileUrl);
  };

  return (
    <>
      {uploading && <span>uploading...</span>}
      <input max-size="1024" disabled={uploading} onChange={onFileChange} type="file" />
      <br />
      <progress max="100" value={progress} />
      <span>{progress}%</span>
      {progress === 100 && !uploading && (
        <>
          <p>url: {url}</p>
          <button type="button" onClick={reset}>
            Reset
          </button>
        </>
      )}
      {message && <p>{message}</p>}
    </>
  );
};

export default Rest;

How to use

In order to use this hook, you must need to create an API endpoint to retrieve a presigned url. These are the most common cloud storage services you can use with this feature:

AWS

Uploading objects using presigned URLs

Docs
Google Cloud

Signed URLs

Docs

This is an example of how to create an s3 instance using aws-sdk in NodeJS:

AWS S3 Setup

import AWS from 'aws-sdk';

AWS.config.update({
  region: '<YOUR-REGION>', // Put your aws region here
  accessKeyId: '<YOUR-ACCESS-KEY-ID>',
  secretAccessKey: '<YOUR-SECRET-ACCESS-KEY>'
});
    
const s3 = new AWS.S3();

export default s3;

Here's an example of how to generate a pre-signed URL using AWS on both types of APIs on NodeJS

Note that these are just examples

The endpoints are the ideal use case that fits with the hook defaults, but they're also configurables depending on the case

Express endpoint

import s3 from 'path/to/s3/file';
import express from 'express';

const app = express();

app.get('/<YOUR-ENDPOINT>', (req, res) => {
  const { filePath, fileType } = req;

  const signedUrl = await s3.getSignedUrlPromise('putObject', {
    Bucket: '<YOUR-S3-BUCKET>',
    Key: filePath,
    Expires: 500, // To prevent future unauthorized uses of URL
    ContentType: fileType,
    ACL: 'public-read'
  });

  res.send({
    signedUrl,
    fileUrl: signedUrl.split(
      // After file path, there is always this param
      // which is assumed constant.
      // This split will left the final file URL
      '?X-Amz-Algorithm='
    )[0]
  });
})

Once you have the API endpoint working endpoint, then, you can use any of the examples listed previously

API Reference

useRestUpload(urlEndpoint[, config])

  • urlEndpoint: It is the URL where you will request the pre signed URL
  • config: Configuration object with the following defaults:
    {
      // Axios config to the urlEndpoint
      // See: https://github.com/axios/axios#request-config
      getSignedUrlOptions: { 
        method: 'get',
      },
      // Axios config to put the file in the signed url
      // See: https://github.com/axios/axios#request-config
      uploadFileOptions: {
        method: 'put',
      },
      // This is the way to specify how to access urlEndpoint response
      // It is default as a recommended implementation and according
      // to the examples previously provided
      resultPath: {
        fileUrl: 'fileUrl',
        signedUrl: 'signedUrl',
      },
    }
    

Recommended urlEndpoint response schema

{
  fileUrl: 'final-file-url.com',
  signedUrl: 'presigned-file-url.com?withSignatureParams=...',
}