Upload files to Amazon S3 from the browser using pre-signed urls

  1. User submits a form with a file from Angular frontend.
  2. Instead of posting the FormData to Node right away, request Node for a AWS S3 pre-signed URL to store this file on S3 bucket with secure bucket policies and permissions with CORS enabled. This pre-signed URL will have an expiry defined and only specific authorized user can generate the pre-signed url.
  3. Once generated this pre-signed url will be sent back to Angular from Node as a response.
  4. Angular will then directly use this pre-signed url to upload the file to AWS S3.
  5. Last but not the least we will enable AWS S3 Bucket Acceleration for upto 3x speed for file transfer. Read more about it here.
npm install aws-sdk
const AWS = require(‘aws-sdk’);
const s3 = new AWS.S3({accessKeyId : config.aws_access_key_id, secretAccessKey : config.aws_secret_access_key, useAccelerateEndpoint: true});
const config = require(‘./config.js’).get(process.env.NODE_ENV);
sudo NODE_ENV=local node server
sudo NODE_ENV=dev node server
sudo NODE_ENV=prod node server
app.get(‘/generatepresignedurl’, function(req,res){
var fileurls = [];

/*setting the presigned url expiry time in seconds, also check if user making the request is an authorized user for your app (this will be specific to your app’s auth mechanism so i am skipping it)*/
const signedUrlExpireSeconds = 60 * 60;
const myBucket = config.s3bucketname;
const myKey = ‘api/uploads/’ + ‘test.csv’;
const params = {Bucket: myBucket, Key: myKey, Expires: signedUrlExpireSeconds, ACL: ‘bucket-owner-full-control’, ContentType:’text/csv’};
s3.getSignedUrl(‘putObject’, params, function (err, url){
if(err){
console.log(‘Error getting presigned url from AWS S3’);
res.json({ success : false, message : ‘Pre-Signed URL error’, urls : fileurls});
}
else{
fileurls[0] = url;
console.log(‘Presigned URL: ‘, fileurls[0]);
res.json({ success : true, message : ‘AWS SDK S3 Pre-signed urls generated successfully.’, urls : fileurls});
}
});
});
  1. Create a Bucket and name it whatever you want, it has to be a DNS compliant.
  2. In Properties, to keep it simple enough we will enable encryption at rest and transfer acceleration.
Encryption settings
{
“Version”: “2012–10–17”,
“Id”: “<policy-id>”,
“Statement”: [
{
“Sid”: “<sid>”,
“Effect”: “Allow”,
“Principal”: {
“AWS”: “arn:aws:iam::<awsaccount>:user/<awsusername>”
},
“Action”: “s3:*”,
“Resource”: “arn:aws:s3:::<s3-bucket-name>”
}
]
}
<?xml version=”1.0" encoding=”UTF-8"?>
<CORSConfiguration xmlns=”http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
onSubmit() {//call node/express api endpoint to generate presignedurl/*fileService is nothing but a service injected into your angular component’s constructor like this constructor(private fileService: FileService) {}*/this.fileService.getpresignedurls().subscribe(res =>{console.log(res); // Your res object will have your pre-signed url//my res object is structured as success: boolean, message: string, urls: Array<string>;if(res.success){const fileuploadurl = res.urls[0];//once the presigned url is received the next service call will upload the file.this.fileService.uploadfileAWSS3(fileuploadurl, ‘text/csv’, this.yourform.get(‘filedetails’).value).subscribe((event: HttpEvent<any>) => {//handle HttpEvent progress or response and update view});}});}
getpresignedurls(): Observable<PreSignedURL>{
let getheaders = new HttpHeaders().set(‘Accept’, ‘application/json’);
return this.http.get<PreSignedURL>(this.getpresignedurlsserver, { headers: getheaders});
}
uploadfileAWSS3(fileuploadurl, contenttype, file): Observable<any>{ //this will be used to upload all csv files to AWS S3 const headers = new HttpHeaders({‘Content-Type’: contenttype});
const req = new HttpRequest(
‘PUT’,
fileuploadurl,
file,
{
headers: headers,
reportProgress: true, //This is required for track upload process
});
return this.http.request(req);
}

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Laravel Image Intervention — Remove Black Background

Nuxt Storybooking: The Painless Way

Testing React Native components with Enzyme

Flutter: Code Organization

Build a Telegram Bot using TypeScript, Node.js, and Telegraf and deploy it on Heroku

GraphQL Code Generator for Typescript React Apollo

Setup Sonarqube for Angular application locally in three easy(!?!) steps

The 4 Phases Of Vue Component Rendering

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Aakash

Aakash

More from Medium

Getting started with Docker

Setting up a simple CI/CD with Django and Gitlab

Asynchronous Servers & Django 3.1: An Explainer

Preventing clickjacking exploits using AWS Cloudfront