Image upload to S3 react native
Tags: serverless, reactnativeimageupload, reactnative, lambda
Published: 2020-08-15 06:15:49

### Introduction
Although I call myself a full stack developer because I can work on any part of the project(from devops to front-end or client delivery may be) but I have a few weak points when it comes to mobile app development. In the process of self-refinement I was getting my hands dirty in mobile app development using react-native lately and I got stuck on a very simple and common task in the mobile apps that’s file uploads.
### Problem Statement
File uploads are easy tasks when a traditional rest API is involved. But I was using something new or not quite a popular approach. I used pre-signed S3 object URL to avoid the loads over my API. Since my API is serverless and uploading files via serverless may become costly in terms of performance and money also. So I have used the API to generate pre-signed URL and uploaded the files directly to the URL from the mobile app itself.
For the reference whole architecture is as below:
![aws serverless architecture](https://dev-to-uploads.s3.amazonaws.com/i/8bhseeu289a96m2a5hx6.png)
Whenever an user wants to upload any file, they first request for the pre-signed URL via API gateway. Then send post request with required parameters directly to the pre-signed URL.
### Back-end Development
I have used flask for my APIs and boto3 to interact with aws services. Boto3 s3 exposes two methods to generate pre-signed URL:
generate_presigned_url → generate a presigned url to get any object
generate_presigned_post → generate a pre-signed url to post any object
There is no clarity on the above points in the official doc as well which took my almost 24 hours of efforts to figure it out.
I used generate_presigned_post to generate pre-signed URL. A piece of the code is as follows:

```
def presigned_url():
    s3 = boto3.client('s3')
    presigned_request = s3.generate_presigned_post(
     os.environ['BUCKET_NAME'],
     'events/myobject',
    Fields={
    'ACL': 'public-read'
    },
    Conditions=[{'acl': 'public-read'}]
    )
    // will return {url: presigned url, fields:a dictionary }
   // for more details refer https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html
    return presigned_request
```
### Front-end Development
I used react-native-image-picker to select image from the device and uploaded the image using the presigned URL which is as follows:
Select image using ImagePicker:
```
ImagePicker.showImagePicker({
    title: title,
    storageOptions: {
        skipBackup: true,
        path: 'images',
    }
}, (response) => {
if (response.didCancel) {
console.log("No image not selected");
    } else if (response.error) {
        console.log('ImagePicker Error: ', response.error);
} else {
        const presignedURL = this.getPresignedURL()
            this.uploadCover(presignedURL.url,response,presignedURL.fields)
    }
});
};
```

### Upload to S3
```
uploadCover = async (url, cover, fields) => {
        let f = new FormData();
f.append("key", fields.key)
        f.append("AWSAccessKeyId", fields.AWSAccessKeyId)
        f.append("x-amz-security-token", fields['x-amz-security-token'])
        f.append("policy", fields.policy)
        f.append("signature", fields.signature)
        f.append("ACL", fields['ACL'])
f.append('file', {
            name: cover.fileName,
            type: cover.type,
            uri: cover.uri
        })
        return fetch(url,
            {
                method: 'POST',
                body: f
            });
    }
```

### My takeaways

* Boto3 s3 client generate_presigned_url does not work for the file upload irrespective of HTTP method you specify
* To upload image to pre-signed URL you have to send the image in post method (binary in body)
* Order of the parameters matter for AWS pre-signed URL.
I hope this isngoing to be helpful for many fellows out there.