Mega Bundle Sale is ON! Get ALL of our React Native codebases at 80% OFF discount ūüĒ•

In this tutorial we will build a React Native app that allows users to upload images and videos from their camera and photo library directly into a AWS S3 bucket. As AWS is the leader of cloud providers, a huge part of the React Native ecosystem is using AWS as the backend for their app. With the release of AWS Amplify, using AWS as backend for a React Native app has never been easier to implement.

The app we are going to build in this React Native AWS tutorial will upload and store image and video files using Amazon S3 bucket. In this article, we will teach you how image and video storage works in React Native using AWS S3 bucket. Every single step of the implementation will be detailed and no stones will be left unturned. So let us get into it.

react-native-aws-s3-bucket

For those who are not familiar with AWS S3 buckets already, here’s is the formal definition of Amazon S3 and what a bucket is:

Amazon S3 stores data as objects within buckets. An object consists of a file and optionally any metadata that describes that file. To store an object in Amazon S3, you upload the file you want to store to a bucket. When you upload a file, you can set permissions on the object and any metadata.

1. Creating the React Native Project

Open a new Terminal instance, and run the following

react-native init s3bucket_storage_example

Then we install and configure the required dependencies, using yarn:

yarn add react-native-image-picker react-native-video @react-native-community/netinfo @react-native-async-storage/async-storage

or using npm:

npm install react-native-image-picker react-native-video @react-native-community/netinfo @react-native-async-storage/async-storage -S

For iOS you also need to install the pods by running

 cd ios && pod install 

Then add the following snippet to your ios/<projectName>/Info.plist to request Photo Library permissions.

<key>NSPhotoLibraryUsageDescription</key>
<string>$(PRODUCT_NAME) would like access to your photo gallery.</string>
2. Picking Photos & Videos from Photo Library

Our first task here is to fetch an image or video from the user’s photo library. In order to achieve that, we are going to leverage¬†react-native-image-picker to pick media files from the library. To be able to display and play videos, we are also going to use the npm package¬†react-native-video. Now, let’s replace all your¬†App.js¬†file with the following source code:

import React, {useState} from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  StyleSheet,
  Alert,
  Image,
} from 'react-native';
import {launchImageLibrary} from 'react-native-image-picker';
import Video from 'react-native-video';

function S3StorageUpload() {
  const [asset, setAsset] = useState(null);

  const selectFile = async () => {
    await launchImageLibrary({mediaType: 'mixed'}, result => {
      if (!result.assets) {
        Alert.alert(result.errorMessage);
        return;
      }
      setAsset(result.assets[0]);
    });
  };

  return (
    <View style={styles.container}>
      <TouchableOpacity onPress={selectFile}>
        <Text style={styles.button}>SELECT {asset ? 'ANOTHER' : ''} FILE</Text>
      </TouchableOpacity>
      {asset ? (
        asset.type.split('/')[0] === 'image' ? (
          <Image
            style={styles.selectedImage}
            source={{uri: asset?.uri ?? ''}}
          />
        ) : (
          <Video
            style={styles.selectedImage}
            source={{uri: asset?.uri ?? ''}}
          />
        )
      ) : null}
      {asset && (
        <>
          <TouchableOpacity onPress={() => setAsset(null)}>
            <Text style={styles.cancelButton}>Remove Selected Image</Text>
          </TouchableOpacity>
        </>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  button: {
    fontSize: 20,
    color: '#fff',
    backgroundColor: 'blue',
    paddingVertical: 20,
    paddingHorizontal: 30,
    marginHorizontal: 20,
    marginVertical: 10,
    textAlign: 'center',
    fontWeight: 'bold',
  },
  cancelButton: {
    backgroundColor: '#fff',
    color: 'blue',
  },
  selectedImage: {
    width: 175,
    height: 200,
    marginTop: 20,
  },
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
});

export default S3StorageUpload;

Now, run your mobile app in your device, simulator or emulator. You will notice that you now are able to pick a photo or a video from your photo library.

3. AWS Amplify Setup

Now that our mobile application UI is pretty much implemented, let’s focus on how to set up the actual backend storage, and how we can integrate the mobile React Native app to communicate with the AWS backend, securely and effectively.

a. Create A New AWS Amplify Project

Head over to the AWS website and create a new AWS Amplify project.

react native s3 bucket

b. Install the AWS Amplify CLI

Next, we need to install AWS Amplify CLI on our local machine by running the following command on your terminal.

npm install -g @aws-amplify/cli

This AWS Amplify CLI will enable us run AWS commands anywhere on our local machine. For example, we can update our local AWS configuration by pulling our AWS backend and updating our backend by pushing local configuration changes to the backend.

c. Pull The AWS Amplify Project into the React Native Codebase

Pull your already created Amplify backend project into your React Native project by running the following command at the root of your React Native project:

amplify pull --appId <appID> --envName <appName>

Note: Please be sure to verify the app login on your Amplify UI Admin, then select your preferred editor, type of app as Javascript, and framework as React Native. You need to enter Y when asked if you wish to modify the backend. Default entries should be allowed for the remaining prompts.

c. Enable Storage in the AWS Amplify Project

At the root of your React Native project run amplify add storage to add storage to your backend project as seen in the below screenshot. After that is done you should push your new project configurations to the backend by running amplify push.

Note: On prompted you should select Content (Images, audio, video, etc.). Adding authentication or functions to your project is not mandatory and every other options can be left as their default value.

3. Upload Photos & Videos to AWS S3 Bucket in React Native 

We’ve finally reached the core part of this tutorial. So far, we set up the React Native project, the AWS backend project, and we built the integration between these two. We’ve also implemented the ability for the users to pick photos from the gallery, so all we need to do now is get those photos and send them over to the S3 cloud.

a. Install aws-amplify Into the React Native Project

yarn add aws-amplify

or

npm install aws-amplify -S

b. Then Import AWS Amplify into Your App.js  

import Amplify, {Storage} from 'aws-amplify';

and configure your AWS Amplify:

import awsconfig from './src/aws-exports';
Amplify.configure(awsconfig); 

c. Add the following two states to the S3StorageUpload

const [progressText, setProgressText] = useState('');
const [isLoading, setisLoading] = useState(false);

progressText will be used to display the progress of the upload and isLoading to help disable our buttons from performing any action while the image is getting uploaded.

d. Add the following handler and helper functions

First we have to convert our URI to a blob using fetchResourceFromURI then uploadResource uploads the file using the asset URI as the key. A success callback is passed to retrieve the key of the uploaded file and this key is important because you need it to get the URI of the resource URI from the backend.

const fetchResourceFromURI = async uri => {
  const response = await fetch(uri);
  console.log(response);
  const blob = await response.blob();
  return blob;
};

const uploadResource = async () => {
  if (isLoading) return;
  setisLoading(true);
  const img = await fetchResourceFromURI(asset.uri);
  return Storage.put(asset.uri, img, {
    level: 'public',
    contentType: asset.type,
    progressCallback(uploadProgress) {
      setProgressText(
        `Progress: ${Math.round(
          (uploadProgress.loaded / uploadProgress.total) * 100,
        )} %`,
      );
      console.log(
        `Progress: ${uploadProgress.loaded}/${uploadProgress.total}`,
      );
    },
  })
    .then(res => {
      setProgressText('Upload Done: 100%');
      setAsset(null);
      setisLoading(false);
      Storage.get(res.key)
        .then(result => console.log(result))
        .catch(err => {
          setProgressText('Upload Error');
          console.log(err);
        });
    })
    .catch(err => {
      setisLoading(false);
      setProgressText('Upload Error');
      console.log(err);
    });
};

4. Trigger the File Upload to AWS S3 in React Native

return (
    <View style={styles.container}>
      <TouchableOpacity onPress={selectFile}>
        <Text style={styles.button}>SELECT {asset ? 'ANOTHER' : ''} FILE</Text>
      </TouchableOpacity>
      {asset ? (
        asset.type.split('/')[0] === 'image' ? (
          <Image
            style={styles.selectedImage}
            source={{uri: asset?.uri ?? ''}}
          />
        ) : (
          <Video
            style={styles.selectedImage}
            source={{uri: asset?.uri ?? ''}}
          />
        )
      ) : null}
      {asset && (
        <>
          <TouchableOpacity onPress={uploadResource}>
            <Text style={styles.button}>UPLOAD</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => setAsset(null)}>
            <Text style={styles.cancelButton}>Remove Selected Image</Text>
          </TouchableOpacity>
        </>
      )}
      <Text>{progressText}</Text>
    </View>
  );

In the above snippet we edit and update the UI by adding a button to trigger the uploadResource function. Here’s a snippet short clip showing how this works.

5. Deleting a File from AWS S3 Bucket in React Native

Deleting a file is straightforward and can be done by a single function call that takes the file key as a required parameters and an optional parameter protectedLevel that specifies the File Access Level of the file when it was uploaded

await Storage.remove('img.png');

6. File Access Levels in AWS

There are 3 File Access Levels namely: public, protected, private. The default level is public for instance when uploading a file using Storage.get('video.mp4') unless configured otherwise as follows:

Storage.configure({ level: 'private' });

According to the official documentation:

  • Public: Accessible by all users of your app. Files are stored under the¬†public/¬†path in your S3 bucket.

  • Protected: Readable by all users, but writable only by the creating user. Files are stored under¬†protected/{user_identity_id}/¬†where the¬†user_identity_id¬†corresponds to the unique Amazon Cognito Identity ID for that user.

  • Private: Only accessible for the individual user. Files are stored under¬†private/{user_identity_id}/¬†where the¬†user_identity_id¬†corresponds to the unique Amazon Cognito Identity ID for that user.

Note

You may get the following error when you run your app: Error: jest-haste-map: Haste module naming collison:

To fix this issue, simply delete the amplify/#current-cloud-backend folder and re-build the app.

Conclusion

In this tutorial, we have successfully been able to set up an AWS Amplify storage project in your AWS Amplify account, then we configured it on your local machine and integrated it with the React Native project. Implementation wise, we built a feature that allows the users to pick photos and videos from their photo gallery and upload it to the S3 bucket cloud. We’ve also learned how to remove those files from the cloud, and also, we got familiar with the various privacy levels for the AWS file cloud.

If you want to skip the tutorial, and jump straight into the code, you can find the Github React Native AWS S3 Bucket project here. If you found this helpful, please give us a star on Github and share this article with your community / your audience.


Leave a Reply

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

Shopping Cart