File Upload with Expo and Firebase

Uploading file with React Native Expo framework and Firebase is not very easy and straight forward, because, file need to be converted to blob format before uploading to Firebase Cloud Storage. The easiest way to deal with file upload with Expo and Firebase is to use AWS S3.

Download Demo App

In this article I will show how to upload file using Expo and Firebase  by building simple News feed App. If you are new to AWS S3 then please check my previous tutorial on Getting Started with AWS S3 .

Below is the code for simple News Feed App build with Expo :

import React from 'react';
import { StyleSheet, Text, View, Dimensions, Button, TextInput, FlatList, Image, ActivityIndicator, YellowBox } from 'react-native';
import * as firebase from 'firebase'
import { ImagePicker } from 'expo';
import { RNS3 } from 'react-native-aws3';
YellowBox.ignoreWarnings(['Setting a timer']);
var _ = require('lodash');

  let config = {
    apiKey: "AIzaSyBtHKZBvCsmqOQhwSFT2QXm-dFV1kO0outy5",
    authDomain: "imageupload.firebaseapp.com",
    databaseURL: "https://imageupload.firebaseio.com",
    projectId: "imageupload"
  };
  let options = {
        keyPrefix: "Album/",
        bucket: "awesometzumt",
        region: "ap-south-2",
        accessKey: "AKIAJPY233YYH4KTMLP",
        secretKey: "TS39s/q4+i+zk2QNqa7jO/QvlwTGYBEc5Db5ty5vz",
        successActionStatus: 201
      }
  firebase.initializeApp(config);

const { width, height } = Dimensions.get('window')

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      text: '',
      photo: null,
      postList : [],
      loading: false,
      loadingPost: true
     }
  }
  componentDidMount(){
      this.firebaseRef = firebase.database().ref('newsfeed')
      this.firebaseRef.on('value', snap => {
        const newsfeed = snap.val()
        const newNewsfeed = _.reverse(_.values(newsfeed));
        this.setState({ postList: newNewsfeed, loadingPost: false })
      })
  }
  async onAddPhoto(){
    let result = await ImagePicker.launchImageLibraryAsync({
     allowsEditing: true,
     aspect: [4, 3],
   });

   if (!result.cancelled) {
     this.setState({ photo: result.uri });
     options["contentType"] = result.type

   }

  }
  onPressPost(){
    this.setState({ loading: true})
    const _this = this
    let post = {}
    post["id"] = firebase.database.ServerValue.TIMESTAMP
    post["text"] = this.state.text
    if(this.state.photo){
      const uri = this.state.photo
      const ext = uri.substr(uri.lastIndexOf('.') + 1);
      const name = Math.round(+new Date()/1000);
      let file = {
        name: name+"."+ext,
        type: "image/"+ext,
        uri
      }

      RNS3.put(file, options).then(response => {
          if(response.status === 201){
            post["photo"] = response.body.postResponse.location
            firebase.database().ref('newsfeed').push(post)
            _this.setState({ text: '', photo: null, loading: false})
          }
      });

    }else {
      firebase.database().ref('newsfeed').push(post)
      _this.setState({ text: '', photo: null, loading: false})
    }

  }
  render() {
    if(this.state.loadingPost)
    return (
      <View style={[styles.container,{justifyContent: 'center'}]}>
        <ActivityIndicator size="large" color="#00ff00" />
      </View>
    )
    else
    return (
      <View style={styles.container}>
        <TextInput
        style={styles.textInput}
        onChangeText={(text) => this.setState({text})}
        value={this.state.text}
        underlineColorAndroid="transparent"
      />
      {
        this.state.loading ? (<ActivityIndicator size="large" color="#0000ff" />):
        (<View style={styles.containerButton}>
          <Button
            onPress={this.onAddPhoto.bind(this)}
            title="Add Photo"
            color="#841584"
          />
          <Button
          onPress={this.onPressPost.bind(this)}
          title="Post"
          color="#841584"
          />
        </View>)
      }
      <FlatList
        showsVerticalScrollIndicator={false}
        data={this.state.postList}
        keyExtractor={item => item.id}
        renderItem={({ item }) =>{
          if(item.photo)
          return(<View style={{paddingTop: 10, marginBottom: 10}}><Text>{ item.text }</Text>
            <Image source={{ uri: item.photo }} style={{ width: width*80/100, height: 200 }} />
            </View>)
          return(<View style={{paddingTop: 10, marginBottom: 10}}><Text>{ item.text }</Text></View>)
        }}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    marginTop: 60
  },
  containerButton: {
    flexDirection: "row",
    justifyContent: "space-between",
    width: 80*width/100
  },
  textInput: {
    height: 40,
    width: 80*width/100,
    borderColor: 'gray',
    borderWidth: 1,
    borderRadius: 10,
    paddingLeft: 5,
    marginBottom: 10
  }
});

In the above code, first we are setting up configuration option for Firebase and then configuration option for AWS S3. In the componentDidMount we are establishing a real time connection with Firebase so that whenever there is any change in ‘newsfeed’ node of database it is reflected in the app. When user taps ‘Add Photo’ button then Expo ImagePicker is launched and the path of the image on mobile is initialized in ‘photo’ state. Since, we are dealing with photos  ‘options[“contentType”]’ will be initialized as ‘image’. ‘contentType’ can be ‘image’, ‘video’, etc.

When user taps ‘Post’ button, we initialize ‘id’ field with a time-stamp and ‘text’ field with a title text entered by the user for the feed photo. Next, we check if user has added photo. If photo is there we extract the extension of image and give it a random unique name. Now, we have name, type and uri of photo which we use with previously intialised AWS options to upload the photo to AWS S3. If upload is successful a ‘201’ response is returned together with image url. So finally we save the id, title and image URL to Firebase db. Because we have real time connection established, the new feed save to database will be automatically shown on the screen.

Download Demo App

Video Tutorial :