2

In my Angular/ Django Rest Framework I want to upload pictures to an Amazon S3 Bucket.

I need to use Presigned URLs since it's faster and more secure than uploading directly client side.

So far I managed to create and retreive the Presigned URL but I am struggling using it for uploading my picture.

This is the view I am using in Django to create the Presigned Url:

class GenerateAwsSignature(View):

    def get(self, request, object_name):
        bucket_name = "moviepictures"
        presigned_url = create_presigned_url(bucket_name, object_name)        
        return JsonResponse({"presigned_url": presigned_url})


def create_presigned_url(bucket_name, object_name,
                         fields=None, conditions=None, expiration=3600):

    load_dotenv()
    AWS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID")
    AWS_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY")

    s3_client = boto3.client('s3',
                             aws_access_key_id=AWS_KEY_ID,
                             aws_secret_access_key=AWS_ACCESS_KEY)
    response = s3_client.generate_presigned_post(bucket_name,
                                                 object_name,
                                                 Fields=fields,
                                                 Conditions=conditions,
                                                 ExpiresIn=expiration)
    return response

My component:

onUpload() {
    const name = this.selectedFile.name
    this.s3Upload.getPresignedUrl(name)
      .subscribe(r => {
        console.log(r)
        this.s3Upload.uploadFile(r, this.selectedFile)
          .subscribe(r => console.log("uploaded :)"))
      })
  }

This is the log of my Presigned URL whenever I want to upload a picture called "Batman" into a bucket called "moviepictures":

presigned_url:
fields:
AWSAccessKeyId: "AKIAYHDIXNIEPY2PQJ6V"
key: "batman"
policy: "eyJleHBpcmF0aW9uIjogIjIwMjEtMDctMDhUMTc6MTY6MTdaIiwgImNvbmRpdGlvbnMiOiBbeyJidWNrZXQiOiAibW92aWVwaWN0dXJlcyJ9LCB7ImtleSI6ICJiYXRtYW4ifV19"
signature: "pCjcs4TE1Qyj62v2KveCoMM1tXo="
__proto__: Object
url: "https://moviepictures.s3.amazonaws.com/"

And the service for uploading to my Bucket:

uploadFile(r: any, selectedFile: any) {
    return this.http.put(r['url'], r['fields'], selectedFile)
  }

Whenever I want to upload I get this error:

TypeError: Cannot read property 'toLowerCase' of undefined

So what is the correct way to use the fields I have to upload my picture in my Bucket?

Toto Briac
  • 908
  • 9
  • 29

1 Answers1

1

After trying with no luck to fix my code I've decided to switch from s3_client.generate_presigned_post() to s3_client.generate_presigned_url(). For more information about their difference, you can check this SO question. Basically instead of getting different fields you get only one url with everything you need for uploading in it. Just don't forget to add a "signature_version" and most important to overwrite the header "content-type" with the file you want to upload MIME type. In my case I add {'content-type': 'imge/jpeg'} to my header because by default it would be 'application/xml' and I would get the SignatureDoesNotMatch error.

This method is working 100% in Postman, so I just have to implement it in my app.

 my_config = Config(
        region_name='eu-west-3',
        signature_version='s3v4',
        retries = {
            'max_attempts': 10,
            'mode': 'standard'
        }
    )


def create_presigned_url(bucket_name, object_name, expiration=3600):

    s3_client = boto3.client('s3',
                            aws_access_key_id="#########",
                            aws_secret_access_key="##########",
                            config=my_config)
    try:
        response = s3_client.generate_presigned_url('put_object',
                                                    Params={'Bucket': bucket_name ,
                                                            'Key': object_name,
                                                            "ContentType": "image/jpeg" },
                                                    ExpiresIn=expiration)
    except ClientError as e:
        logging.error(e)
        return None

    return response

url = create_presigned_url("bemytest", "batman.jpg")
Toto Briac
  • 908
  • 9
  • 29