
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.
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.
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 theuser_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 theuser_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.