Upload Image/Video To Flask Backend From React Native App (Expo App)

Let’s see how to send Images and Video from React Native Expo to Flask

Sreehith Manubolu
9 min readMay 27, 2021

--

Photo by Justin Kauffman on Unsplash

Introduction

There are many articles on how to upload Image/Video from React Native to Node js backend. This article mainly focuses on how to send these to a Python background server in our case its Flask.

I would like to start from the beginning of the Expo and Flask installation (readers may refer to the Contents section and can skip this part).

In the below implementation, the user can see the button displayed on the screen which enables the user to select a media file from the gallery and send it to the flask server. The button is held in disable mode until the response is received from the server.

Contents

  • Installing Expo and Flask
  • Front-end
  • Back-end
  • Running the App
  • Random Chit-Chat

Installing Expo and Flask

For beginners who are trying to develop a mobile app using React Native, Expo can be treated as the best choice to start with. Expo is both a framework and a platform to develop, build and deploy React applications on both iOS and Android with a single JS/TS code-base.

Flask is a python framework that is used for developing backend applications. It is suitable for building a RESTful API which we are going to code in further sections.

  1. Create a root folder that contains both frontend and backend folders using the command. Here in our case let’s start with “project”.
mkdir project
cd project
mkdir frontend backend

2. Navigate to project/frontend and install Expo CLI with command npm instal --global expo-cli . Expo is installed globally on your system now it's time to create a project out of it, type the command expo init . and it shows you some templates to start, I prefer to start with the blank from the managed workflow section. If everything went well you should be able to see the folder contents containing some files and folders like

  • app.json — As we have chosen managed workflow, we don’t have native iOS or Android projects to change the configurations of the app, expo will do it for us. Simply, it contains the configurations of the App like app name, icons, etc. For more details refer to app.json.
  • assets — This folder contains assets of the project. Any file that does not come under the code but required for the runtime of the app is considered an asset like icons, fonts, logos, etc.
  • App.js — The main js file required to run the app.
  • babel.config.js — babel is nothing but a js transpiler that can convert the code into compatible versions of browsers. React uses JSX and ES6 which the browser couldn’t understand so here babel does the job in converting these to vanilla JS. babel.config.js is a configuration file for the babel. (at present you need not worry about it)
  • package.json — this file is used in almost every npm project, it contains metadata like authors, description of the project, license info, scripts, libraries, dependencies, and their versions used in the project.
  • package-lock.json — package.json contains the libraries info like ^2.3.1 this ^ symbol tells that the project needs the respective libraries version to be at least 2.3.1 to work. package-lock.json contains the currently installed version. For more details refer to package.jsonvspackage-lock.json
  • node_modules — this folder contained the installed libraries from the package.json file.

Installations are done for the frontend now let’s jump to the backend.

3. Navigate to project/backend . It's good to set up a virtual environment before you start any python project.

#installing venv module
sudo apt install python3-venv
#setting up virtual environment named "v_env"
python3 -m venv v_env
#activating the venv
source v_env/bin/activate

After activating the virtual environment you can see (v_env) before each prompt, this tells that the current running environment is changed to v_env . Now it is ideally a blank workspace where you can install python packages by using pip3 . To list the installed requirements of the project you can use pip3 freeze .

Now install the packages flask , flask_cors and create a blank app.py file by the below commands.

pip3 install flask
pip3 install Flask-Cors
touch ./app.py

Front-end

Here we come to the most interesting part of the project. I have made the code look short in a single App.js file to simply understand the main functionality (uploading files to the Flask server).

Currently, In this article, our App will be coded in a single class component (you can even use functional Components). Now I’ll explain each part in the code and present you the final App.js at last.

Our state variable contains two keys named cameraRollPer and disableButton which signifies the camera roll permissions and the working status of the button (enable/disable) respectively.

If an instance of a component is created, React calls the methods of that component in the following order

  • constructor()
  • render()
  • componentDidMount()

After creating the component, whenever the state changes (by using this.setState() ) the methods are called in the following order

  • render()
  • componentDidUpdate()

It’s good to have one place for all the permissions in the App. I prefer to keep them in componentDidMount().

Its time to set up a render(), that displays a button when the permissions are granted for cameraRoll and when they are not, it should provide an appropriate msg on the rendered screen.

Now upon seeing the above code you may have wondered about this.state.diableButton and this.pickMedia() . Like I said earlier the button has two states disable and enable, button gets disabled upon selecting the image/video from the Camera Roll and gets enabled only upon receiving a response from the flask server. This is just to make sure that the user doesn't click on that button continuously. this.pickMedia() is a method that uses expo-image-picker and upon clicking the button, this pops up a new interface provided by the device itself to select an image/video from the Camera Roll.

Now we need to install expo-image-picker in our, Expo App using the command expo install expo-image-picker , you will see that the package.json is updated.

this.pickMedia() at first, disables the button and will launch the device Interface to pick media from Camera Roll, using ImagePicker.launchImageLibraryAsync() .

result is the name of returned resolved or rejected Promise. Now the URI along with other properties of the selected Image/Video is sent to this.toServer() function.

I will explain why I am dealing with base64 at the end of the article, here it is irrelevant and we won’t even use it in this.toServer() function, treat it as just an unused variable that we are sending to this.toServer() function and don't even worry about it.

this.toServer() is a function that uses expo-file-system which need to be installed in our Expo app by using the command expo install expo-file-system and imported in our App.js by import * as FS from “expo-file-system”; . In this function, this.toServer() we will send the data of the selected media file to the Flask server, one thing we need to know is now we only have the URI of the selected media file but not the whole data of that file. Here FS.uploadAsync() comes to save the day, this takes 3 arguments first it's the URL to the server route, the second is the URI of a file in the File System (FS) of the device and the third is the configuration settings for sending the HTTP Request.

If you refer to the below code, you may see that I have not used localhost but some other 192.168.1.6 to run the backend Flask server, this selection is explained later.

The content-type header is dynamically changed based upon the selected media file whether it is Image or Video, httpMethod is POST and the encoding format to send the file pointing to the given URI is BINARY_CONTENT

Below is the code for App.js

Back-end

Don’t worry backend won’t be as long as Front-end. Backend is also a single app.py file that contains two routes /image and /video which detects the respective media and encode the acquired BINARY_CONTENT in a single media file in the same directory ie project/backend

In both routes, request.get_data() is used to get the BINARY_CONTENT sent from React Native. You can open any file in 'wb' mode and can write this binary output and then a response of datatype string is sent to Front-end

Running The App

With our Front-end and Back-end ready finally we can see how the app works. It’s good to open two different terminals as we need to run two different servers. For understanding purposes, I will be referring to TER-1 as the Front-end terminal and TER-2 as the Back-end terminal.

  1. Navigate to project/frontend in TER-1. Run the command expo start , this command should take some time and start a metro bundler in the default browser. For reference see below

To run the app on your mobile device iOS or Android, you need to first install Expo Go App from PlayStore/AppStore. If you are in Android you can directly scan the QR code(appearing in the above image) from your device, but if you are in iOS you need to login to Expo to see your current Apps that are in Development.

2. Navigate to project/backend in TER-2. Activate the virtual environment using the command source v_env/bin/activate , if you wanna deactivate it at any time just type deactivate on the terminal. Now we can start the app in two types one on localhost just by typing flask run and the other is on the desired host by typing flask run -h <hostnumber> .By default Flask will run the file on port 5000 . Remember as you are running flask run the name of the file should be app.py , if you want you can also run the file by python3 app.py # or some other named.py but you need to specify the host in the app.py itself.

if __name__ == '__main__':
app.run(host=YOUR_DESIRED_HOST, port=5000)

Notice one thing here your Back-end is running on your Laptop but we need to send a request from the mobile, If I would have written host as localhost in Front-end then the mobile would try to access the localhost which is meaningless as our Back-end is running on another machine (Laptop) and not on a mobile phone, this is the reason why I won’t directly run my backend in localhost . so you can see in the Front-end that I used some other host like 192.168.1.6 instead of localhost. we can use the host that is generated by Expo itself (highlighted in red in the above pic ) and run the Back-end on the same host but on a different port and can mention this host in the Front-end code after starting a metro bundler, this won't be a problem if the Back-end is already deployed because there would be a fixed URL that can be incorporated in code. The most necessary part apart from all of this is your mobile and Laptop should be on the same Network.

The final conclusions and steps from the above paragraphs are

  1. First, start a metro bundler by expo start in TER-1.
  2. Copy only the host value ( exp://192.168.1.6:19000 this is URL highlighted number is the host ) from metro bundler.
  3. Paste the host in the desired variable in Front-end code ie let host = “192.168.1.6”( as you will be running the Back-end in this host).
  4. Run the command flask run -h 192.168.1.6 to start the backend
  5. Open Expo Go App on iOS or Android device and run the developed app by scanning the QR code( only Android ) or by logging into your Expo account from both laptop and mobile

Note that the host values changes on changing the Network, and it is basically IPv4 of your connected Wi-Fi

That all of it, Enjoy the app by tweaking it!

Random Chit-Chat

  • Here I am gonna talk about the above base64 Part. When I was going through a project of mine, I was finding resources on how to upload files to Flask server from React Native but unfortunately, I couldn’t find one but after digging some, I thought of sending the media files as base64 strings, this is ok for sending Images but many resources are suggesting not to use this for Video transmission as it would increase the data size that needs to be sent 33% which makes it difficult to transmit. But before knowing it I have done that thing and later thought of this approach. Below code converts a base64 string to file using python (this may not be useful here, just for reference)
  • While using expo-camera maybe you have come across the issues that if we use Camera in iOS it is working fine but when used in Android the Expo Go app immediately crashes if we take a photo, This happened to me and I found it somewhere in a comments section that we can use useCamera2Api prop for Android Platform to make it use one of the 2 available camera Apis. you can read more from here.
  • If you wanna learn js, I suggest you start with this Codecademy course. At first, it may seem a bit slow because it covers from the root but if you go further you may like it. (another point Its completely free)

--

--