12

I am developing a feature to upload file to S3 using Angular/presigned url and API Gateway/Lambda to generate presigned url.

My workflow is described as following:

  1. Get selected file from template
  2. Request my api (gateway/Lambda) to generate presigned URL using file name.

const body = { fileName: this.selectedFile.name }

const preSignedUrl = await this.http.post('https://xxxxx.execute-api.eu-west-1.amazonaws.com/dev/v1/profile/avatar', body).toPromise();

  1. Post the file in PUT mode on the generated presigned url.

Backend

I am using serverless (Lambda/API gateway) to calculate the presignedUrl.

Lambda

const AWS = require('aws-sdk')
 
 
module.exports.uploadLargeFile = async (event) => {  
 
  console.log('1. Event: ', event.body);
  const reqBodyAsString = event.body || '{}';
  const fileName = JSON.parse(reqBodyAsString).fileName;
 
  return getUploadURL(fileName);
}
 
const getUploadURL = async (fileName) => {
  const s3Params = {
      Bucket: process.env.AVATAR_BUCKET,
      Expires: 60 * 60,
      ACL: 'public-read',
      Key: fileName 
  };
 
  const s3 = new AWS.S3();    
  try {
      const presinedUrl =  await s3.getSignedUrl('putObject', s3Params);
      return utiles.buildResponse(200, {presinedUrl })
  } catch (error) {
      return utiles.buildResponse(500, {error: 'facing some errors' })
 
  }
};

S3 CORS configuration

<?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>

Frontend

Template

 <input  type="file" (change)="onFileSelected($event)"/>
 <button (click)="onUpload()" > Upload to S3 </button>  

Component

selectedFile: File = null;  
  constructor(private http: HttpClient ) {}
 
  onFileSelected(event:any){
    this.selectedFile = <File> event.target.files[0]; 
  }
 
  async onUpload() {   
    console.log('1. SelectedFile: ', this.selectedFile); 
    const body = { fileName: this.selectedFile.name }
    const preSignedUrl = await this.http.post('https://xxxxx.execute-api.eu-west-1.amazonaws.com/dev/v1/profile/avatar', body).toPromise();  
    console.log('2. PreSignedURL: ', preSignedUrl)
    console.log('3. Upoloading File (binary) to S3')
 
    const upload = this.http.put(preSignedUrl.presinedUrl, this.selectedFile).toPromise();    
    upload.then(data => {
      console.log('=> ', data )
    }).catch(err => console.log('error: ', err))
  }

Questions

This code works fine when I use postman (posting binary file).

enter image description here

But when I use Angular I got this error :

HttpErrorResponse {headers: HttpHeaders, status: 403, statusText: "Forbidden", url: "https://bucket-photos-10042018-bucket.s3.eu-west-1.ama…Dlt4WBiu0uLfK8b0%2FWmT9xcKz6jOa0KpQxnWMqm4A%3D%3D", ok: false, …}error: "<?xml version="1.0" encoding="UTF-8"?>↵<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>ASIA5PS6D3MHY4DGELXH</AWSAccessKeyId><StringToSign>PUT↵↵image/png↵1593696048↵x-amz-acl:public-read↵x-amz-security-token:IQoJb3JpZ2luX2VjEMX//////////wEaCWV1LXdlc3QtMSJHMEUCIARoXCbv7BKP9dVn5vhETAol3/qS0737isaWI25J1Uq+AiEAyzIe32BN9KfxcJhg1V5tZnxy6OatGUcOP7zXB0b2Wa8q6gEIXRABGgw5MjY4MzY1NzkwODciDGqkpU2I2+WNMsxTNCrHATPQ4MNNbrijo7GDZ/2zeUYgvBHshXkQQvcNVTBgD0PhKoSJ7VZsCa48J0aYAFXrpsGLrqZeXZkZjAhMxnXUGTFH3ymC2ZFgsHE1V8OOdhbbOSbBeKU7w92WDMNuF8SadrGT/Xl3uHUw/UPVzit5StD15T5sAjTbW4m7SYxRoEFXGq43IN62GOKQDgsaM2MMjSzzEEzY2agcKL8rNlySfWubZuqAKHfplGxz7yRH1uDshSV0uAzwLKdUDi0E1JjwkoyDDOs7mfAwn6L39wU64AGPsqOCEweNrGk1nscphBKiWKYFaht4RQM9Gqt236NXkHQBedxD7Xd3ZJlzpm/dCTyxN75DVLidggrYSTmc8GU4Nl6Rfe3UQ8L4aABSOPgGu5GlA61gelwmPstdTBPgYtzjpoM1ZlNi7MWImslkkpgLqFl1Ls1OtK1alcLF5bghXj6aIEweS6MYVGQiayaCO/0lYh1x7Vyz4A9qiSd5zWO5cwS2jz714p2JljabubYBuWfXhBDpwPYkCA6KXpzvfP3qfDlt4WBiu0uLfK8b0/WmT9xcKz6jOa0KpQxnWMqm4A==↵/dev-inmates-photos-bucket/aws-logo.png</StringToSign><SignatureProvided>JFfuW+hlCyKjZ1vOt3YsxqktikY=</SignatureProvided><StringToSignBytes>50 55 54 0a 0a 69 6d 61 67 65 2f 70 6e 67 0a 31 35 39 33 36 39 36 30 34 38 0a 78 2d 61 6d 7a 2d 61 63 6c 3a 70 75 62 6c 69 63 2d 72 65 61 64 0a 78 2d 61 6d 7a 2d 73 65 63 75 72 69 74 79 2d 74 6f 6b 65 6e 3a 49 51 6f 4a 62 33 4a 70 5a 32 6c 75 58 32 56 6a 45 4d 58 2f 2f 2f 2f 2f 2f 2f 2f 2f 2f 77 45 61 43 57 56 31 4c 58 64 6c 63 33 51 74 4d 53 4a 48 4d 45 55 43 49 41 52 6f 58 43 62 76 37 42 4b 50 39 64 56 6e 35 76 68 45 54 41 6f 6c 33 2f 71 53 30 37 33 37 69 73 61 57 49 32 35 4a 31 55 71 2b 41 69 45 41 79 7a 49 65 33 32 42 4e 39 4b 66 78 63 4a 68 67 31 56 35 74 5a 6e 78 79 36 4f 61 74 47 55 63 4f 50 37 7a 58 42 30 62 32 57 61 38 71 36 67 45 49 58 52 41 42 47 67 77 35 4d 6a 59 34 4d 7a 59 31 4e 7a 6b 77 4f 44 63 69 44 47 71 6b 70 55 32 49 32 2b 57 4e 4d 73 78 54 4e 43 72 48 41 54 50 51 34 4d 4e 4e 62 72 69 6a 6f 37 47 44 5a 2f 32 7a 65 55 59 67 76 42 48 73 68 58 6b 51 51 76 63 4e 56 54 42 67 44 30 50 68 4b 6f 53 4a 37 56 5a 73 43 61 34 38 4a 30 61 59 41 46 58 72 70 73 47 4c 72 71 5a 65 58 5a 6b 5a 6a 41 68 4d 78 6e 58 55 47 54 46 48 33 79 6d 43 32 5a 46 67 73 48 45 31 56 38 4f 4f 64 68 62 62 4f 53 62 42 65 4b 55 37 77 39 32 57 44 4d 4e 75 46 38 53 61 64 72 47 54 2f 58 6c 33 75 48 55 77 2f 55 50 56 7a 69 74 35 53 74 44 31 35 54 35 73 41 6a 54 62 57 34 6d 37 53 59 78 52 6f 45 46 58 47 71 34 33 49 4e 36 32 47 4f 4b 51 44 67 73 61 4d 32 4d 4d 6a 53 7a 7a 45 45 7a 59 32 61 67 63 4b 4c 38 72 4e 6c 79 53 66 57 75 62 5a 75 71 41 4b 48 66 70 6c 47 78 7a 37 79 52 48 31 75 44 73 68 53 56 30 75 41 7a 77 4c 4b 64 55 44 69 30 45 31 4a 6a 77 6b 6f 79 44 44 4f 73 37 6d 66 41 77 6e 36 4c 33 39 77 55 36 34 41 47 50 73 71 4f 43 45 77 65 4e 72 47 6b 31 6e 73 63 70 68 42 4b 69 57 4b 59 46 61 68 74 34 52 51 4d 39 47 71 74 32 33 36 4e 58 6b 48 51 42 65 64 78 44 37 58 64 33 5a 4a 6c 7a 70 6d 2f 64 43 54 79 78 4e 37 35 44 56 4c 69 64 67 67 72 59 53 54 6d 63 38 47 55 34 4e 6c 36 52 66 65 33 55 51 38 4c 34 61 41 42 53 4f 50 67 47 75 35 47 6c 41 36 31 67 65 6c 77 6d 50 73 74 64 54 42 50 67 59 74 7a 6a 70 6f 4d 31 5a 6c 4e 69 37 4d 57 49 6d 73 6c 6b 6b 70 67 4c 71 46 6c 31 4c 73 31 4f 74 4b 31 61 6c 63 4c 46 35 62 67 68 58 6a 36 61 49 45 77 65 53 36 4d 59 56 47 51 69 61 79 61 43 4f 2f 30 6c 59 68 31 78 37 56 79 7a 34 41 39 71 69 53 64 35 7a 57 4f 35 63 77 53 32 6a 7a 37 31 34 70 32 4a 6c 6a 61 62 75 62 59 42 75 57 66 58 68 42 44 70 77 50 59 6b 43 41 36 4b 58 70 7a 76 66 50 33 71 66 44 6c 74 34 57 42 69 75 30 75 4c 66 4b 38 62 30 2f 57 6d 54 39 78 63 4b 7a 36 6a 4f 61 30 4b 70 51 78 6e 57 4d 71 6d 34 41 3d 3d 0a 2f 64 65 76 2d 69 6e 6d 61 74 65 73 2d 70 68 6f 74 6f 73 2d 62 75 63 6b 65 74 2f 61 77 73 2d 6c 6f 67 6f 2e 70 6e 67</StringToSignBytes><RequestId>DD6A5C2957A647FF</RequestId><HostId>pXY2D5gEXY9+G81Tq/VwTEdMk3zTq4fCAsRmAjhneCgpt2buFQwxGScbjASC4vHfDSH/eK8mlGg=</HostId></Error>"headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}message: "Http failure response for https://bucket-photos-10042018-bucket.s3.eu-west-1.amazonaws.com/aws-logo.png?AWSAccessKeyId=ASIA5PS6D3MHY4DGELXH&Expires=1593696048&Signature=JFfuW%2BhlCyKjZ1vOt3YsxqktikY%3D&x-amz-acl=public-read&x-amz-security-token=IQoJb3JpZ2luX2VjEMX%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCWV1LXdlc3QtMSJHMEUCIARoXCbv7BKP9dVn5vhETAol3%2FqS0737isaWI25J1Uq%2BAiEAyzIe32BN9KfxcJhg1V5tZnxy6OatGUcOP7zXB0b2Wa8q6gEIXRABGgw5MjY4MzY1NzkwODciDGqkpU2I2%2BWNMsxTNCrHATPQ4MNNbrijo7GDZ%2F2zeUYgvBHshXkQQvcNVTBgD0PhKoSJ7VZsCa48J0aYAFXrpsGLrqZeXZkZjAhMxnXUGTFH3ymC2ZFgsHE1V8OOdhbbOSbBeKU7w92WDMNuF8SadrGT%2FXl3uHUw%2FUPVzit5StD15T5sAjTbW4m7SYxRoEFXGq43IN62GOKQDgsaM2MMjSzzEEzY2agcKL8rNlySfWubZuqAKHfplGxz7yRH1uDshSV0uAzwLKdUDi0E1JjwkoyDDOs7mfAwn6L39wU64AGPsqOCEweNrGk1nscphBKiWKYFaht4RQM9Gqt236NXkHQBedxD7Xd3ZJlzpm%2FdCTyxN75DVLidggrYSTmc8GU4Nl6Rfe3UQ8L4aABSOPgGu5GlA61gelwmPstdTBPgYtzjpoM1ZlNi7MWImslkkpgLqFl1Ls1OtK1alcLF5bghXj6aIEweS6MYVGQiayaCO%2F0lYh1x7Vyz4A9qiSd5zWO5cwS2jz714p2JljabubYBuWfXhBDpwPYkCA6KXpzvfP3qfDlt4WBiu0uLfK8b0%2FWmT9xcKz6jOa0KpQxnWMqm4A%3D%3D: 403 Forbidden"name: "HttpErrorResponse"ok: falsestatus: 403statusText: "Forbidden"url: "https://bucket-photos-10042018-bucket.s3.eu-west-1.amazonaws.com/aws-logo.png?AWSAccessKeyId=ASIA5PS6D3MHY4DGELXH&Expires=1593696048&Signature=JFfuW%2BhlCyKjZ1vOt3YsxqktikY%3D&x-amz-acl=public-read&x-amz-security-token=IQoJb3JpZ2luX2VjEMX%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCWV1LXdlc3QtMSJHMEUCIARoXCbv7BKP9dVn5vhETAol3%2FqS0737isaWI25J1Uq%2BAiEAyzIe32BN9KfxcJhg1V5tZnxy6OatGUcOP7zXB0b2Wa8q6gEIXRABGgw5MjY4MzY1NzkwODciDGqkpU2I2%2BWNMsxTNCrHATPQ4MNNbrijo7GDZ%2F2zeUYgvBHshXkQQvcNVTBgD0PhKoSJ7VZsCa48J0aYAFXrpsGLrqZeXZkZjAhMxnXUGTFH3ymC2ZFgsHE1V8OOdhbbOSbBeKU7w92WDMNuF8SadrGT%2FXl3uHUw%2FUPVzit5StD15T5sAjTbW4m7SYxRoEFXGq43IN62GOKQDgsaM2MMjSzzEEzY2agcKL8rNlySfWubZuqAKHfplGxz7yRH1uDshSV0uAzwLKdUDi0E1JjwkoyDDOs7mfAwn6L39wU64AGPsqOCEweNrGk1nscphBKiWKYFaht4RQM9Gqt236NXkHQBedxD7Xd3ZJlzpm%2FdCTyxN75DVLidggrYSTmc8GU4Nl6Rfe3UQ8L4aABSOPgGu5GlA61gelwmPstdTBPgYtzjpoM1ZlNi7MWImslkkpgLqFl1Ls1OtK1alcLF5bghXj6aIEweS6MYVGQiayaCO%2F0lYh1x7Vyz4A9qiSd5zWO5cwS2jz714p2JljabubYBuWfXhBDpwPYkCA6KXpzvfP3qfDlt4WBiu0uLfK8b0%2FWmT9xcKz6jOa0KpQxnWMqm4A%3D%3D"__proto__: HttpResponseBase

Could you please help me to understand from where this error is comming ?

it looks like something missing in the request to match the signature ?

Thank you in advance for your help.

1
  • 1
    This question itself is a good help for me Commented Apr 25, 2021 at 7:30

1 Answer 1

12

Finaly, I found the reason why it was not working :) The contentType was missing during the generation of presignedUrl;

const s3Params = {
    Bucket: process.env.AVATAR_BUCKET,
    Expires: 60 * 60,
    ACL: 'public-read',
    Key: fileName,
    ContentType: 'image/jpeg' // need to be done dynamically
};
Sign up to request clarification or add additional context in comments.

1 Comment

Your question did help me a lot. If you could take some time out to help me answer this question, it would be very helpful stackoverflow.com/questions/65225397/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.