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.
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 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
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.
Video Tutorial :