How to Upload a File to AWS S3 in React(typescript)
I’m using a library called Dropzone to be able to upload files by drag and drop in the browser.
The following is the site I referred to.
1. Upload using sdk to a public bucket
If the upload destination is a public bucket, you can use the aws-sdk to upload files using the access_key and secret_key with the appropriate permissions.
Load the SDK and add the authentication information. The credentials are listed in the .env, so load that environment variable.
AWS authorization settings and uploads
import AWS from 'aws-sdk'
require('dotenv').config()
const AWS_ACCESS_KEY = process.env.REACT_APP_AWS_ACCESS_KEY;
const AWS_SECRET_KEY = process.env.REACT_APP_AWS_SECRET_KEY;
const BUCKET = process.env.BUCKET;
AWS.config.update({
accessKeyId: AWS_ACCESS_KEY,
secretAccessKey: AWS_SECRET_KEY,
region: 'ap-northeast-1'
});
const s3 = new AWS.S3();
const upload_image = (file: any, bucket: string) => {
try {
const params = {
Bucket: bucket,
Key: file.name,
ContentType: file.type,
Body: file,
};
const res = s3.putObject(params).promise();
} catch (error) {
console.log(error);
return;
}
}
2. Upload using presigned-url (signed URL)
If the file to be uploaded cannot be made public, you can ask the server to issue a signed URL with a time limit, and then perform an HTTP PUT request to that URL to upload the file.
Issuing a signed URL
Here is the code for issuing a signed URL on the server side. I often work on machine learning development projects and use python for backend development, so this is a python example. The backend API is made with AWS Lambda, so it looks like that.
server side code
import boto3, json
from botocore.client import Config
def lambda_handler(event, context):
Try:
body = json.loads(event['body'])
file_name = body['file_name'].
file_type = body['file_type'])
s3 = boto3.client('s3', config=Config(signature_version='s3v4'))
file_upload_key = '{0}/upload/{1}'.format('prd', file_name)
file_upload_url = s3.generate_presigned_url(
ClientMethod='put_object',
Params={'Bucket': S3_INBOX_1DAY, 'Key': file_upload_key},
ExpiresIn=300,
HttpMethod='PUT')
body = {
'Presigned_url': str(file_upload_url)
}
res = {
'isBase64Encoded': False,
'body': json.dumps(body),
}
return res
except Exception as e:
res = {
'isBase64Encoded': False,
'body': json.dumps(''),
}
return res
HTTP PUT request with axios
We will send an HTTP request from javascript to the backend server. For this purpose, we use the HTTP library, axios. The usage is as follows.
uploadImage = (file: any) => {
const params = {
file_type: file.type,
file_name: file.name
}
const options = {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}
return axios.post(
'https://[domain]/api/v1/xxxxxxx', params, options)
.then(res => {
const img_options = {
headers: {
'Content-Type': file.type
}
};
return axios.put(res.data.file_upload_url, file, img_options);
})
}
Rendering implementation with Dropzone
Screen Print Screen
render() {
return (
<div>.
<h1>File Upload</h1>.
<Dropzone onDrop={this.handleOnDrop} multiple maxSize={8000000} accept="image/*" >
{({ getRootProps, getInputProps }) => (
<div
{... .getRootProps({ className: "dropzone" })}
style={baseStyle}
>
<input {. .getInputProps()} />
{this.state.isUploading ?
<div>Uploading a file</div> :
<div>Drag or click on a file to select it</div> }
</div>.
)}
</Dropzone>.
</div>.
);
}
```.
## Conclusion
Finally, I have included a summary of the code so far.
This code includes two cases: one is to upload the file to S3 using the SDK and the other is to upload the file using an http request using presigned-url.
```typescript
import * as React from "react";
import Dropzone from 'react-dropzone';
import axios from 'axios';
import CSS from 'csstype';
import AWS from 'aws-sdk'
require('dotenv').config()
const AWS_ACCESS_KEY = process.env.REACT_APP_AWS_ACCESS_KEY;
const AWS_SECRET_KEY = process.env.REACT_APP_AWS_SECRET_KEY;
const BUCKET = process.env.BUCKET;
AWS.config.update({
accessKeyId: AWS_ACCESS_KEY,
secretAccessKey: AWS_SECRET_KEY,
region: 'ap-northeast-1'
});
const s3 = new AWS.S3();
interface INbdProps {
}
interface INbdState {
isUploading: boolean;
images: any[];
}
const upload_image = (file: any, bucket: string) => {
try {
const params = {
Bucket: bucket,
Key: file.name,
ContentType: file.type,
Body: file,
};
const res = s3.putObject(params).promise();
} catch (error) {
console.log(error);
return;
}
}
const baseStyle: CSS.Properties = {
flex: 1,
display: "flex",
flexDirection: "column",
alignItems: "center",
padding: "20px",
// borderWidth: 2,
// borderRadius: 2,
borderColor: "#eeeeee",
borderStyle: "dashed",
backgroundColor: "#fafafa",
color: "#bdbdbd",
outline: "none",
transition: "border .24s ease-in-out"
};
class UFileUpload extends React.Component<INbdProps, INbdState> {
constructor(props: INbdProps) {
super(props);
this.state = {
isUploading: false,
images: []
};
this.handleOnDrop = this.handleOnDrop.bind(this);
}
handleOnDrop(files: any[]) {
Promise.all(files.map(file => this.uploadImage(file)))
.then(images => {
this.setState({
isUploading: false,
images: this.state.images.concat(images)
});
}).catch(e => console.log(e));
}
uploadImage = (file: any) => {
upload_image(file, '[bucket]')
const params = {
file_type: file.type,
file_name: file.name
}
const options = {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}
return axios.post(
'https://[domain]/api/v1/xxxxxxx', params, options)
.then(res => {
const img_options = {
headers: {
'Content-Type': file.type
}
};
return axios.put(res.data.file_upload_url, file, img_options);
})
}
render() {
return (
<div>
<h1>File Upload</h1>
<Dropzone onDrop={this.handleOnDrop} multiple maxSize={8000000} accept="image/*" >
{({ getRootProps, getInputProps }) => (
<div
{...getRootProps({ className: "dropzone" })}
style={baseStyle}
>
<input {...getInputProps()} />
{this.state.isUploading ?
<div>ファイルをアップロードしています</div> :
<div>ファイルをドラックするかクリックしてファイルを選択してください</div>}
</div>
)}
</Dropzone>
</div>
);
}
}
export default UFileUpload;