Skip to content

Commit

Permalink
Add example for Uppy with S3 and a Node.js server (#4129)
Browse files Browse the repository at this point in the history
Co-authored-by: Merlijn Vos <merlijn@soverin.net>
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
  • Loading branch information
3 people authored Oct 17, 2022
1 parent a315381 commit 3f07d79
Show file tree
Hide file tree
Showing 19 changed files with 366 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ module.exports = {
'*.mjs',
'e2e/clients/**/*.js',
'examples/aws-companion/*.js',
'examples/aws-presigned-url/*.js',
'examples/aws-php/*.js',
'examples/bundled/*.js',
'examples/custom-provider/client/*.js',
'examples/digitalocean-spaces/*.js',
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dist/
lib/
coverage/
examples/dev/bundle.js
examples/aws-presigned-url/vendor/*
examples/aws-php/vendor/*
test/endtoend/create-react-app/build/
test/endtoend/create-react-app/coverage/
uppy-*.tgz
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ Released: 2022-08-02
- @uppy/transloadit: send `assembly-cancelled` only once (Antoine du Hamel / #3937)
- meta: `keepNames` in bundle (Antoine du Hamel / #3926)
- meta: e2e: fix Transloadit test suite with Cypress 10 (Antoine du Hamel / #3936)
- meta: Bump guzzlehttp/guzzle from 7.4.1 to 7.4.5 in /examples/aws-presigned-url (dependabot[bot] / #3842)
- meta: Bump guzzlehttp/guzzle from 7.4.1 to 7.4.5 in /examples/aws-php (dependabot[bot] / #3842)
- @uppy/tus: fix dependencies (Antoine du Hamel / #3923)
- meta: doc: fix linter failure in `image-editor.md` (Antoine du Hamel / #3924)
- meta: doc: Fix typo in image-editor.md (Ikko Ashimine / #3921)
Expand Down Expand Up @@ -841,7 +841,7 @@ Released: 2022-01-10
- @uppy/tus: pause all requests in response to server rate limiting (Antoine du Hamel / #3394)
- @uppy/transloadit: better defaults for rate limiting (Antoine du Hamel / #3414)
- @uppy/companion: Fix Companion deploys (kiloreux / #3388)
- meta: update aws-presigned-url example to use esm (Antoine du Hamel / #3413)
- meta: update aws-php example to use esm (Antoine du Hamel / #3413)
- @uppy/image-editor: namespace input range css (Merlijn Vos / #3406)
- @uppy/screen-capture: Add missing option to the screen capture types (Mustafa Navruz / #3400)
- @uppy/drag-drop: fix `undefined is not a function` TypeError (Antoine du Hamel / #3397)
Expand Down
102 changes: 102 additions & 0 deletions examples/aws-nodejs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Uppy + AWS S3 with Node.JS

A simple and fully working example of Uppy and AWS S3 storage with Node.js (and Express.js) It uses presigned URL at the backend level.

This README file starts with AWS as this one need to be resolved first.

# AWS Configuration

It's assumed that you are familiar with AWS, at least, with the storage service (S3) and users & policies (IAM).

These instructions are not fit for production but tightening the security is out of the scope here.

## S3 Setup

- Create new S3 bucket in AWS (e.g. `aws-nodejs`).
- Add a bucket policy.
```{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicAccess",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::aws-nodejs/*"
}
]
}
```

- Make the S3 bucket public.
- Add CORS configuration.
```[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"PUT",
"HEAD",
"POST",
"DELETE"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]
```

## AWS Credentials

You may use existing AWS credentials or create a new user in the IAM page.

- Make sure you setup the AWS credentials properly and write down the Access Key ID and Secret Access Key.
- You may configure AWS S3 credentials using [environment variables](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/loading-node-credentials-environment.html) or a [credentials file in `~/.aws/credentials`](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/setting-credentials-node.html).
- You will need at least `PutObject` and `PutObjectAcl` permissions.
```{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectAcl"
],
"Resource": "arn:aws:s3:::aws-nodejs/*"
}
]
}
```
# Install

Download this code or clone repository into a folder and install dependencies:

```bash
corepack yarn install
```

Add a `.env` file to the root directory and define the S3 bucket name and port variables like the example below:

```
S3_BUCKET=aws-nodejs
PORT=8080
```

# Enjoy it

Start the application:

```bash
corepack yarn workspace @uppy-example/aws-nodejs start
```

Dashboard demo should now be available at http://localhost:8080.

You have also a Drag & Drop demo on http://localhost:8080/drag.

*Feel free to check how the demo works and feel free to open an issue.*
53 changes: 53 additions & 0 deletions examples/aws-nodejs/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require('dotenv').config({ path: path.join(__dirname, '..', '..', '.env') })

const express = require('express')

const app = express()
const path = require('node:path')

const port = process.env.PORT
const bodyParser = require('body-parser')

const aws = require('aws-sdk')

app.use(bodyParser.json())

app.get('/', (req, res) => {
const htmlPath = path.join(__dirname, 'public', 'index.html')
res.sendFile(htmlPath)
})

app.get('/drag', (req, res) => {
const htmlPath = path.join(__dirname, 'public', 'drag.html')
res.sendFile(htmlPath)
})

app.post('/sign-s3', (req, res) => {
const s3 = new aws.S3()
const fileName = req.body.filename
const { contentType } = req.body
const s3Params = {
Bucket: process.env.S3_BUCKET,
Key: fileName,
Expires: 60,
ContentType: contentType,
ACL: 'public-read',
}

s3.getSignedUrl('putObject', s3Params, (err, data) => {
if (err) {
console.log(err)
return res.end()
}
const returnData = {
url: data,
method: 'PUT',
}
res.write(JSON.stringify(returnData))
res.end()
})
})

app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
17 changes: 17 additions & 0 deletions examples/aws-nodejs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "@uppy-example/aws-nodejs",
"version": "1.0.0",
"description": "Uppy for AWS S3 with a custom Node.js backend for signing URLs",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"author": "",
"license": "MIT",
"dependencies": {
"aws-sdk": "^2.1038.0",
"body-parser": "^1.20.0",
"dotenv": "^16.0.0",
"express": "^4.18.1"
}
}
85 changes: 85 additions & 0 deletions examples/aws-nodejs/public/drag.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Uppy</title>
<link href="https://releases.transloadit.com/uppy/v3.0.1/uppy.min.css" rel="stylesheet">
</head>
<body>
<section class="example">
<div id="drag-drop-area"></div>
<div class="for-ProgressBar"></div>
<div class="uploaded-files">
<h5>Uploaded files:</h5>
<ol></ol>
</div>
<script type="module">
import {Uppy, DragDrop, ProgressBar, AwsS3 } from "https://releases.transloadit.com/uppy/v3.0.2/uppy.min.mjs"

// Function for displaying uploaded files
const onUploadSuccess = (elForUploadedFiles) => (file, response) => {
const url = response.uploadURL
const fileName = file.name

const li = document.createElement('li')
const a = document.createElement('a')
a.href = url
a.target = '_blank'
a.appendChild(document.createTextNode(fileName))
li.appendChild(a)

document.querySelector(elForUploadedFiles).appendChild(li)
}

var uppy = new Uppy({
autoProceed: true,
restrictions: {
maxNumberOfFiles: 10,
}
})
.use(DragDrop, {
inline: true,
target: '#drag-drop-area'
})
.use(ProgressBar, { target: '.example .for-ProgressBar', hideAfterFinish: true })
.use(AwsS3, {
getUploadParameters (file) {
// Send a request to our PHP signing endpoint.
return fetch('/sign-s3', {
method: 'post',
// Send and receive JSON.
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({
filename: file.name,
contentType: file.type,
}),
}).then((response) => {
// Parse the JSON response.
return response.json()
}).then((data) => {
// Return an object in the correct shape.
return {
method: data.method,
url: data.url,
fields: data.fields,
// Provide content type header required by S3
headers: {
'Content-Type': file.type,
},
}
})
},
});

uppy.on('complete', (result) => {
console.log('Upload complete! We’ve uploaded these files:', result.successful)
});

uppy.on('upload-success', onUploadSuccess('.example .uploaded-files ol'));
</script>
</section>
</body>
</html>
58 changes: 58 additions & 0 deletions examples/aws-nodejs/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Uppy</title>
<link href="https://releases.transloadit.com/uppy/v3.0.1/uppy.min.css" rel="stylesheet">
</head>
<body>
<div id="drag-drop-area"></div>
<script type="module">
import { Uppy, Dashboard, AwsS3 } from "https://releases.transloadit.com/uppy/v3.0.2/uppy.min.mjs"
var uppy = new Uppy()
.use(Dashboard, {
inline: true,
target: '#drag-drop-area',
})
.use(AwsS3, {
getUploadParameters (file) {
// Send a request to our PHP signing endpoint.
return fetch('/sign-s3', {
method: 'post',
// Send and receive JSON.
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify({
filename: file.name,
contentType: file.type,
}),
}).then((response) => {
// Parse the JSON response.
return response.json()
}).then((data) => {
// Return an object in the correct shape.
return {
method: data.method,
url: data.url,
fields: data.fields, // For presigned PUT uploads, this should be left empty.
// Provide content type header required by S3
headers: {
'Content-Type': file.type,
},
}
})
},
});

uppy.on('complete', (result) => {
console.log('Upload complete! We’ve uploaded these files:', result.successful)
})

uppy.on('upload-success', (file, data) => {
console.log('Upload success! We’ve uploaded this file:', file.meta['name'])
})
</script>
</body>
</html>
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@uppy-example/aws-presigned-url",
"name": "@uppy-example/aws-php",
"version": "0.0.0",
"dependencies": {
"@uppy/aws-s3": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ This example also uses the AWS PHP SDK.
To install it, [get composer](https://getcomposer.org) and run `composer update` in this folder.

```bash
corepack yarn workspace @uppy-example/aws-presigned-url exec "composer update"
corepack yarn workspace @uppy-example/aws-php exec "composer update"
```

Configure AWS S3 credentials using [environment variables](https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html#environment-credentials) or a [credentials file in `~/.aws/credentials`](https://docs.aws.amazon.com/aws-sdk-php/v3/guide/guide/credentials.html#credential-profiles).
Expand All @@ -26,7 +26,7 @@ Configure a bucket name and region in the `s3-sign.php` file.
Then, again in the **repository root**, start this example by doing:

```bash
corepack yarn workspace @uppy-example/aws-presigned-url start
corepack yarn workspace @uppy-example/aws-php start
```

The demo should now be available at http://localhost:8080.
Expand All @@ -37,5 +37,5 @@ You can use a different S3-compatible service like GCS by configuring that servi
AWS_PROFILE="gcs" \
COMPANION_AWS_ENDPOINT="https://storage.googleapis.com" \
COMPANION_AWS_BUCKET="test-bucket-name" \
corepack yarn run example aws-presigned-url
corepack yarn run example aws-php
```
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion website/src/docs/aws-s3.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ uppy.use(AwsS3, {
})
```

See the [aws-presigned-url example in the uppy repository](https://github.com/transloadit/uppy/tree/main/examples/aws-presigned-url) for a small example that implements both the server-side and the client-side.
See the [aws-php example in the uppy repository](https://github.com/transloadit/uppy/tree/main/examples/aws-php) for a small example that implements both the server-side and the client-side.

### Retrieving presign parameters of the uploaded file

Expand Down
Loading

0 comments on commit 3f07d79

Please sign in to comment.