7
The Firebase storage NPM module provides access to the Firebase app instance to create a reference object that is used to upload images to the Firebase storage. The Firebase storage bucket can store the data using a file or folder. A reference
instance can also take a string path value to upload an image to Firebase storage.
This guide explains the steps to upload a selected image from a device to Firebase storage using the image picker helper functions. Check out these previous guides for more context and complete details:
Upload Images to Firebase Storage in React Native (this guide)
The @react-native-firebase/storage
module provides access to the FirebaseApp
instance that is used to upload media content into the Firebase storage. A storage()
instance contains all the necessary details to verify and establish a connection with the Firebase server app to access storage contents.
Follow these steps to upload an image to Firebase storage:
ref
function to get a reference instance to upload an image.putFile
function with a file path/URI to upload the file to Firebase storage. puFile
function returns a task
object that supports Promise
based implementation to track the result of the upload process using the then
function.1 2 3 4 5 6 7 8 9 10
import storage from '@react-native-firebase/storage'; // 1 uploadImageToStorage(path, imageName) { let reference = storage().ref(imageName); // 2 let task = reference.putFile(path); // 3 task.then(() => { // 4 console.log('Image uploaded to the bucket!'); }).catch((e) => console.log('uploading image error => ', e)); }
The reference
instance also supports different file formats, such as put(Blob | Uint8Array | ArrayBuffer)
, and provides a putString
to upload different types of data.
The image picker implementation can be optimized with the Platform.select
function. A response
object can be used to retrieve platform-specific values and the name of the file:
file:///
schema in path value, whereas Android uses a plain URI to access images. The different file path or URI values can be handled using the Platform
API to verify the OS type at run time:1 2 3 4 5 6 7 8 9
/** * Get platform specific value from response */ getPlatformPath({ path, uri }) { return Platform.select({ android: { path }, ios: { uri } }) }
Note: It's recommended to use platform APIs as any change in API can break our custom logic. Also, the image path should be formatted to support the file:///
protocol.
response
object provides the name of the file using the response.fileName
property, which can be used to store the images with default names instead of custom names.It is important to inform the user about the image loading process. This can be done using the ActivityIndicator
component to show a circular progress indicator on the UI. The progress indicator is shown during the image uploading process. Follow these steps to implement it:
isLoading
property to track the uploading task status.isLoading
to true
at the beginning of the uploading task and set it to false
inside the then
and catch
.ActivityIndicator
and display it using the isLoading
value: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
// App.js /** * @author Pavneet Singh */ import React from "react"; import { StyleSheet, View, Button, Image, ActivityIndicator, Platform, SafeAreaView, Text, } from "react-native"; import storage from '@react-native-firebase/storage'; import ImagePicker from 'react-native-image-picker'; export default class App extends React.Component { state = { imagePath: require("./img/default.jpg"), isLoading: false, status: '', } chooseFile = () => { this.setState({ status: '' }); var options = { title: 'Select Image', customButtons: [ { name: 'customOptionKey', title: 'Choose Photo from Custom Option' }, ], storageOptions: { skipBackup: true, // do not backup to iCloud path: 'images', // store camera images under Pictures/images for android and Documents/images for iOS }, }; ImagePicker.showImagePicker(options, response => { if (response.didCancel) { console.log('User cancelled image picker', storage()); } else if (response.error) { console.log('ImagePicker Error: ', response.error); } else if (response.customButton) { console.log('User tapped custom button: ', response.customButton); } else { let path = this.getPlatformPath(response).value; let fileName = this.getFileName(response.fileName, path); this.setState({ imagePath: path }); this.uploadImageToStorage(path, fileName); } }); }; getFileName(name, path) { if (name != null) { return name; } if (Platform.OS === "ios") { path = "~" + path.substring(path.indexOf("/Documents")); } return path.split("/").pop(); } uploadImageToStorage(path, name) { this.setState({ isLoading: true }); let reference = storage().ref(name); let task = reference.putFile(path); task.then(() => { console.log('Image uploaded to the bucket!'); this.setState({ isLoading: false, status: 'Image uploaded successfully' }); }).catch((e) => { status = 'Something went wrong'; console.log('uploading image error => ', e); this.setState({ isLoading: false, status: 'Something went wrong' }); }); } /** * Get platform specific value from response */ getPlatformPath({ path, uri }) { return Platform.select({ android: { "value": path }, ios: { "value": uri } }) } getPlatformURI(imagePath) { let imgSource = imagePath; if (isNaN(imagePath)) { imgSource = { uri: this.state.imagePath }; if (Platform.OS == 'android') { imgSource.uri = "file:///" + imgSource.uri; } } return imgSource } render() { let { imagePath } = this.state; let imgSource = this.getPlatformURI(imagePath) return ( <SafeAreaView style={styles.container}> {this.state.isLoading && <ActivityIndicator size="large" style={styles.loadingIndicator} />} <View style={styles.imgContainer}> <Text style={styles.boldTextStyle}>{this.state.status}</Text> <Image style={styles.uploadImage} source={imgSource} /> <View style={styles.eightyWidthStyle} > <Button title={'Upload Image'} onPress={this.chooseFile}></Button> </View> </View> </SafeAreaView> ) } } const styles = StyleSheet.create({ container: { flex: 1, width: '100%', height: '100%', backgroundColor: '#e6e6fa', }, imgContainer: { alignItems: 'center', justifyContent: 'center', position: 'absolute', width: '100%', height: '100%' }, eightyWidthStyle: { width: '80%', margin: 2, }, uploadImage: { width: '80%', height: 300, }, loadingIndicator: { zIndex: 5, width: '100%', height: '100%', }, boldTextStyle: { fontWeight: 'bold', fontSize: 22, color: '#5EB0E5', } });
react-native-image-picker
also supports custom buttons with the customButtons
option property to fetch images from other platforms or custom social media APIs.1 2 3
alternately allow read; allow write: if request.auth != null;
Read more about the Firebase rules here.
Firebase is specifically optimized to address issues on mobile devices with many other great services. It is important to integrate Firebase configuration and rules (or authentication services) carefully. React Native offers all the required NPM plugins to integrate Firebase services and required features to fetch images, check network status, and much more. The complete optimized codebase is available on the RNFirebaseStorage repository. Happy coding!
7