How to Upload Images in S3 Bucket Using React
Intro
In this blog post I volition detail how to leverage AWS Amplify, AWS Appsync and Amazon S3 to provide an image upload capability into a react native app.
This blog assumes that the reader is familiar with AWS Amplify and its characteristic categories, however if y'all are not familiar then please have a read of this great intro by @ajonp one of our AWS community members.
I will also be working off an existing AWS backend awarding that has already been created with the AWS Amplify CLI and has had both the 'auth' and 'api' characteristic categories added. You tin follow along with this cosmos by going to the official Amplify react-native docs. Or ;tldr it and simply clone this repo
Notation - if you do end up cloning the repo, remember to run 'amplify configure' and 'amplify init' to re-configure the project to a new cloud back cease in your own AWS account.
What are nosotros building ?
The base application nosotros will be adding photo/image storage to should look something like the below image.
There are 2 views in this app
- A list of Todo's that have already been created.
- A form to create a new Todo
Clicking on 'New To Do' button on the 'list view' will bring you to the 'create view' and once a new todo has been created the app will navigate back to the 'list view' from the 'create view'
So, aught besides catchy here, what nosotros are going to add to this app is the ability to save images to our cloud backend when in the 'create view' and the ability to see those images in the 'listing view'.
Adding the image concept to backend resource.
Offset thing is confirming what dilate characteristic categories we have installed - to notice this out we simply run the dilate condition
command which gives the following output
Equally you can see, we have both auth and api feature categories installed - so that ways that there is an Amazon Cognito instance treatment users and auth, an AWS Appsync example handling the GraphQL API and an Amazon DynamoDB instance treatment data persistence.
Adding an S3 storage bucket.
First affair is to add the new Storage characteristic category to this existing backend. This is super piece of cake to exercise past running the dilate add together storage
command in the terminal and selecting the post-obit options
Note - most of these are defaults apart from the 'Who should have admission' question where I have selected Auth users just and given them full CRUD privilege.
Adding an 'image' to our API definition
The adjacent step is to add together the concept of an image to our GraphQL schema definition. You can notice the schema definition under ./amplify/backend/api/myapi/schema.graphql
We simply add together a new String type called image - which will concur our image proper name when we persist an image and an image pre-signedURL when nosotros retrieve an prototype. The schema should now expect similar this
Note: if we desire to add FGAC to any of the fields in the schema we could apply the '@auth' transformer directive - for more detail on access control, transformer directives and @auth [read this article] https://aws.amazon.com/blogs/mobile/graphql-security-appsync-dilate/ - by Brice Pelle.
Once that has been updated, we become back to our terminal and run amplify update api
control to regenerate the api definition. The output from this command should look similar to the below:
Once consummate we finally need to telephone call amplify push --y
to push button all of our local changes into our cloud back end. This means that out dorsum-end services will now look like the following architectural diagram with the addition of the S3 bucket that the Amplify CLI Storage category has provisioned.
Updating the react native client awarding
So that is enough messing around in the last, now information technology'southward time to write some code. We volition be making all the changes in a single file App.js and the get-go thing we volition do is add in some additional state to store the image name and the imageURI we want to add together to each todo. So, something similar
const initialTodoState = { name : '' , description : '' , epitome : '' } const initialAppState = { showForm : faux , imageURI : '' }
Notation - All of the code for the finished app tin can be plant here
Selecting Images
Once we accept that we need to write a function that locates images on the device and displays them for the user to select. Luckily there is a tertiary-political party library chosen React Native Image Picker that will do most of this for us. And so, let's create a part to call this library, it should expect something like this.
handleChoosePhoto = async () => { endeavor { launchImageLibrary ({ mediaType : ' photo ' , includeBase64 : false , maxHeight : 200 , maxWidth : 200 , }, ( response ) => { if ( response . uri ) { updateAppState ( ' imageURI ' , response . uri ) const filename = uuid . v4 () + ' _todoPhoto.jpg ' updateTodoState ( ' prototype ' , filename ) } }) } grab ( mistake ) { console . log ( error ) } }
The handleChoosePhoto
office is calling the launchImageLibrary
function, that is office of react-native-image-picker's api. We are simply passing in some options like, image type , image height and image width and from the response we become the imageURI ( the location on the image on the device ) and nosotros relieve this and a unique image name in the state fields we created before.
Annotation: Nosotros a using the react-native-uuid library to make sure all images have unique names - this makes it easier when nosotros shop these names in DynamoDB.
When implemented the 'handleChoosePhoto' UI looks like this:
Uploading Images
Once we select the epitome, we can at present create a Todo in the 'create view' that contains the image we have selected by only adding the following tag in the App.js 'render' function
< Image source = {{ uri : todo . prototype }} manner = { styles . image } / >
which should look something similar this after adding in the appropriate tag.
Nosotros adjacent need to implement the office that is called past the 'Create Todo' button. This addTodo()
part is where we add together the image upload code to the existing GraphQL api phone call as shown beneath
async role addTodo () { try { // new code for images const photo = await fetch ( appstate . imageURI ) const photoBlob = wait photo . blob (); await Storage . put ( todoState . image , photoBlob , { level : ' private ' , contentType : ' image/jpg ' }) //existing api lawmaking const todo = { ... todoState } setTodos ([... todos , todo ]) wait API . graphql ( graphqlOperation ( createTodo , { input : todo })) setTodoState ( initialTodoState ) setAppState ( initialAppState ) } grab ( err ) { console . log ( ' error creating todo: ' , err ) } }
Here we are fetching the blob of the photo we have selected using the stored imageURI and so passing the result along with the unique image proper name to the Dilate Storage.put()
call. Not forgetting as we are using a new Amplify category of 'Storage' we need to import the relevant libraries at the top of App.js in the imports department
import { API , graphqlOperation , Storage } from ' aws-amplify '
Once we have stored the blob in S3 using Storage.put()
the last part of the function persists the image name along with all the other todo information in DynamoDB via the AppSync GraphQL api call, before finally re-setting whatever state.
Downloading Images
So now we have our image uploaded as part of the 'create view', the terminal step is to view the images when our initial 'list view' displays. It is currently looking very empty
To implement this, we first of all add together in an prototype tag to the UI that renders the list.
< Image source = {{ uri : todo . epitome }} fashion = { styles . image } />
And then nosotros update the fetchTodos
function to retrieve the relevant image for every Todo we accept in our list.
async function fetchTodos () { try { //fetch the recipes from the server const todoData = expect API . graphql ( graphqlOperation ( listTodos )); let todos = todoData . data . listTodos . items // for all todos get the pre-signURL and store in images field todos = await Promise . all ( todos . map ( async ( todo ) => { const imageKey = expect Storage . get ( todo . image , { level : ' individual ' }) console . log ( imageKey ) todo . image = imageKey ; return todo ; })); setTodos ( todos ) setTodoState ( initialTodoState ) setAppState ( initialAppState ) } take hold of ( err ) { console . log ( ' error fetching todos ' ) + err } }
And that is it!! your app should at present await like this, where the prototype is downloaded into the app from the S3 bucket using the pre-signed URL that Storage.get()
so conveniently generates.
Conclusion
And then that is it, in a few minutes nosotros have added an image upload and download feature into our existing react native application.
All nosotros needed to exercise is update our backend using the Amplify CLI to provision our S3 bucket and change the schemas of our DynamoDB table and Appsync API. Then using the Dilate client libraries write a Storage.put() function and a Storage.get() function to add together and retrieve the images. The effect was not only fast to achieve, only also came with security built in.
Thanks for reading, hopefully this makes calculation this kind of feature to your react native application a breeze - if y'all would similar me to go into more than detail on any of the features of AWS Amplify or indeed annihilation AWS related in a future post - but post in the comments beneath.
Besides, whatever feedback is ever welcome either hither or catch me on the socials.
Source: https://dev.to/aws/adding-image-upload-to-a-react-native-app-with-aws-amplify-4j7m
0 Response to "How to Upload Images in S3 Bucket Using React"
Post a Comment