November 24, 2017 ademcan One comment

Upload/download images to/from AWS S3 in React-Native: a step-by-step guide

I recently struggled a lot to be able to upload/download images to/from AWS S3 in my React Native iOS app. I wanted to allow users to upload images to S3 and access some of the images from other users… I have a server running on Express (nodejs) and here is how I solved my problem.

1. Create an IAM user

Go to the IAM section on your AWS dashboard, select the Users category from the left side menu and press the Add user button.
1.1 Enter a username (test_user in this example) and select the access type Programmatic access, click Next:Permissions.

1.2 Leave this section as is and click Next:Review.

1.3 Finalize the creation of the user by clicking the Create user button.

1.4 Your new IAM user is created, click the Download .csv button and keep the file somewhere safe as it contains the Secret Access Key of your user. We will need this information later to create pre-signed URLs.

2. Create an AWS S3 bucket

Go to the S3 section on your AWS dashboard and click the + Create bucket button.
2.1 Enter a name for your bucket (test-bucket-tutorial in this example), the name has to be unique. Click Next.

2.2 Leave the default parameters in the Set properties section and click Next (you can always change them later)

2.3 Same for Set permissions, leave the default parameters and click Next

2.4 Review and click Create bucket

2.5 Go to your freshly created bucket and click + Create folder . Name your new folder images.

3. Create policy for your IAM user

So far we created our IAM user and our S3 bucket with an images folder. We now need to give permissions to our IAM user to access the S3 test-bucket-tutorial/images folder.
3.1 Go to your IAM section on your AWS dashboard and click the Policies section.

3.2 Click Create policy and select the JSON tab, then copy-paste the text below on the text input.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": ["arn:aws:s3:::test-bucket-tutorial/images"]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:GetObjectAcl",
        "s3:GetObjectVersion",
        "s3:PutObjectAcl",
        "s3:PutObjectVersionAcl"
      ],
      "Resource": ["arn:aws:s3:::test-bucket-tutorial/images/*"]
    }
  ]
}

It should look like the picture below:

3.3 Click Review policy, give a name to your policy (test-bucket-tutorial-policy in this example) and click Create policy

4. Attach policy to your IAM user

Now we need to attach the policy to the user.
4.1 On the Policies section, select our freshly created policy.

4.2 Go to the Attached entities tab and click Attach

4.3 Select the IAM user that we created at Step1 and click Attach policy

Now the user has the permissions (listed in the policy) to access the test-bucket-tutorial/images folder in your S3 bucket.

5. Generating pre-signed URL on the server side

Pre-signed URLs allow you to create a unique URL based on the credentials that you will provide and to safely use this URL on your client-side (without providing the Secret Access Key). As written earlier, I am using expressjs as a back-end, however the code below can easily be adapted to other alternatives.
5.1 Install the node aws-sdk package.

npm install aws-sdk

5.2 Copy-paste the following code to your main javascript code when running your server, this will output the generated pre-signed URL to the console.

var AWS = require('aws-sdk');
var s3 = new AWS.S3({accessKeyId:'XXXXXXXXXXXX', secretAccessKey:'YYYYYYYYYYYY', region:'REGION'});

var params = {Bucket: 'test-bucket-tutorial', Key: 'images/myimage.jpg', ContentType: 'image/jpeg'};
s3.getSignedUrl('putObject', params, function (err, url) {
    console.log('Your generated pre-signed URL is', url);
});
  • Replace XXXXXXXXXXXX by your user’s Access Key ID that you can find on the csv file previously downloaded (Step 1.4)
  • Replace YYYYYYYYYYYY by your user’s Secret Access Key that you can find on the csv file previously downloaded
  • Replace REGION by the region of your AWS S3 bucket, you can find this information here

6. Upload images to S3 from the client-side

My client-side is implemented in React-Native. To allow the user to select a picture and send it to our S3 bucket we will use the ImagePickerIOS component (which is not very highly documented…). Put the code below on your RN app wherever you want it to be run.

ImagePickerIOS.openSelectDialog({}, imageUri => {
    const xhr = new XMLHttpRequest()
    xhr.open('PUT', presignedUrl)
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
            console.log('Image successfully uploaded to S3')
        } else {
            console.log('Error while sending the image to S3')
        }
    }
}
xhr.setRequestHeader('Content-Type', 'image/jpeg')
xhr.send({ uri: imageUri, type: 'image/jpeg', name: 'myimage.jpg'})
}, error => console.log(error));

I adapted the original code that I found in this very helpful article. Change the presignedUrl value with the pre-signed URL that you generated on your server (Step 5.2).

7. Downloading images from the client-side

If you want to download some images to your client app, you need to follow a similar approach with some slight changes:

  • remove the ContentType attribute from the params array
  • use the getObject method instead of putObject

The code will then look like:

var params = {Bucket: 'test-bucket-tutorial', Key: 'images/myimage.jpg'};
s3.getSignedUrl('getObject', params, function (err, url) {
    console.log('Your generated pre-signed URL is', url);
});

This code will generate a unique URL to access your image.
Check the AWS.S3 Documentation page if you want to learn more about the different methods that are available.

8. A concrete example

I personally use these examples in my own project as follow :
1 – The user selects a photo from the Gallery
2 – The RN app sends a request to my Express server with the name (and ID) of the user as the image filename
3 – The nodejs server generates the pre-signed URL adapted to the image filename using the AWS SDK
4 – The server sends the generated pre-signed URL to the RN app
5 – The RN app performs a HTTP request with the pre-signed URL to upload the image to S3

Let me know how it goes for you in the comments 🙂

One thought on “Upload/download images to/from AWS S3 in React-Native: a step-by-step guide

Leave a Reply

Your email address will not be published. Required fields are marked *