This post is intended for web developers who are trying to implement image upload functionality.
If your website needs to allow users to upload images then it is always a good idea to upload them on s3 bucket, instead of your dedicated server. Uploading on s3 will have following benifits:
To upload images on S3, We need to have AWS_ACCESS_KEY and AWS_SECRET_KEY of aws user who has appropriate permission to upload images in bucket.
Earlier, To store images on s3, developers where used to send file bytes to back-end server and then that server is responsible to upload images to s3 bucket because we can not use AWS_SECRET_KEY in front-end.
But there is a better way possible. We can use Fine Uploader library which allows us to upload images directly to s3 with minimum support of back-end. This library eliminates server side complexity and saves bandwidth.
I will quickly explain how to use this library with angular app.
First of all, we need to create bucket in AWS S3. By default, AWS allows cross-origin GET requests on S3 bucket. This is enforced via an XML document in the CORS configuration. You need to change this configuration to allow POST request on S3 bucket.
1<?xml version=”1.0" encoding=”UTF-8"?>
2<CORSConfiguration xmlns=”http://s3.amazonaws.com/doc/2006-03-01/">
3 <CORSRule>
4 <AllowedOrigin>*</AllowedOrigin>
5 <AllowedMethod>POST</AllowedMethod>
6 <MaxAgeSeconds>3000</MaxAgeSeconds>
7 <AllowedHeader>*</AllowedHeader>
8 </CORSRule>
9</CORSConfiguration>
AWS s3 bucket CORS editor
Go to your Angular project’s directory and run following command.
npm install fine-uploader --save
Now create upload button with unique id in your app component html file.
1<div class=”btn” id=”upload_image”>Upload image</div>
Import fine uploader in your component.ts file.
1import { s3 } from ‘fine-uploader/lib/core/s3’;
Now, you need to initialize fine uploader inside ngAfterViewInit method for your component’s life-cycle. You can set options as per your need. Read detailed explanation about options here.
Sample file to showing fine uploader initialization and setting options:
1import { Component, AfterViewInit, OnInit } from '@angular/core';
2
3import { s3 } from 'fine-uploader/lib/core/s3';
4
5@Component({
6 selector: 'app-root',
7 templateUrl: './app.component.html',
8 styleUrls: ['./app.component.css']
9})
10export class AppComponent implements AfterViewInit{
11 bucketName = 'fineuploader-demo';
12
13 uploader: any;
14
15 ngAfterViewInit() {
16 let instance = this;
17 this.uploader = new s3.FineUploaderBasic({
18 button: document.getElementById('upload_image'),
19 debug: false,
20 autoUpload: true,
21 multiple: true,
22 validation: {
23 allowedExtensions: ['jpeg', 'jpg', 'png', 'gif', 'svg'],
24 sizeLimit: 5120000 // 50 kB = 50 * 1024 bytes
25 },
26 region: 'us-west-2',
27 request: {
28 endpoint: 'https://' + instance.bucketName +'.s3.amazonaws.com/',
29 params: { 'Cache-Control': 'private, max-age=31536000, must-revalidate' }
30 },
31 signature: {
32 endpoint: 'http://localhost:8000/api/v1/fine_uploader/s3_signature/',
33 },
34 iframeSupport: {
35 localBlankPagePath: '/somepage.html'
36 },
37 cors: {
38 expected: true,
39 sendCredentials: true
40 },
41 objectProperties: {
42 acl: 'public-read',
43 },
44 callbacks: {
45 onSubmit: function (id, fileName) {
46 console.log('selected file:', fileName);
47 },
48 // onSubmitted: function(id, name) { alert('onSubmitted');},
49 onComplete: function (id, name, responseJSON, maybeXhr) {
50 if(responseJSON.success) {
51 console.log('upload complete', name);
52 console.log('uploaded image url', 'https://' + instance.bucketName + '.s3.amazonaws.com/' + this.getKey(id));
53 }
54 },
55 // onAllComplete: function (successful, failed) { console.log(failed); },
56 // onCancel: function (id, name) {},
57 // onUpload: function(id, name) { alert('onUpload');},
58 // onUploadChunk: function(id, name, chunkData) { alert('onUploadChunk');},
59 // onUploadChunkSuccess: function(id, chunkData, responseJSON, xhr) { alert('onUploadChunkSuccess');},
60 // onResume: function(id, fileName, chunkData) { alert('onResume');},
61 // onProgress: function (id, name, loaded, total) {},
62 // onTotalProgress: function(loaded, total) { alert('onTotalProgress');},
63 // onError: function (id, name, reason, maybeXhrOrXdr) { },
64 // onSessionRequestComplete: function (response, success, xhrOrXdr) { }
65 }
66 });
67 }
68}
Also read: Cache Busting for an Angular App Deployed with AWS S3 and CloudFront
Take a look at options which I have set here in order to upload images to S3.
Below is the code snippet written in Django to show sample endpoint which generates signature.
1import boto3
2from rest_framework import status
3from rest_framework.response import Response
4from rest_framework.views import APIView
5
6class GenerateAwsSignature(APIView):
7 """Generate an AWS signature for presigned POST requests.
8 This signed data allows file uploads directly from the browser to S3.
9 """
10
11 def post(self, request):
12 """
13 * API to be consumed by fine uploader to upload images directly to s3 bucket.
14
15 """
16
17 s3 = boto3.client(
18 's3',
19 aws_access_key_id='xxxxxxxxxxxxxxx',
20 aws_secret_access_key='xxxxxxxxxxxxxx')
21 # http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Client.generate_presigned_post
22 try:
23 data = s3.generate_presigned_post(
24 'fineuploader-demo',
25 '${filename}',
26 Conditions=request.data['conditions'],
27 ExpiresIn=60 * 60,
28 )
29 except KeyError:
30 return Response(zo_diageo_response.generate_failure_response(
31 custom_error_codes.INVALID_DATA_1204),
32 status=status.HTTP_400_BAD_REQUEST)
33
34 return Response({'policy': data['fields']['policy'],
35 'signature': data['fields']['signature'],
36 'url': data['url']},
37 status=status.HTTP_200_OK)
Done. You can check running your front-end app and click on that upload button which we have created. Your selected files will be uploaded to S3 and you will see uploaded image urls in your browser’s console.
That’s it. Thank you for reading.
Also read: Dynamically Adding and Removing Form Fields in Angular's Reactive Forms with FormArray