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

Prototypes, inheritance and why people say that in javascript everything is an object.

Reasons to Fall in Love With JavaScript: Meet Aframe

Intro to Stacks

Making a Flutter Todo App from Scratch Part 2

Create A Vuex Undo/Redo Plugin For VueJS

How do Testing in Ionic 6 with React and Karma

How to apply Software Architecture Design Principles in React applications? Part 1

Flutter: Code Organization

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

Comparisons of Auth solutions

A Simple Explanation Of What A WebSocket Is

SE3050 -Application Frameworks Blog — 03

The Benefits of Node.js over Java