firebasereact-nativegoogle-cloud-storagereact-native-firebasereact-native-fetch-blob

RNFetchBlob is not working to upload image


I am trying to make a upload function for my Firebase Cloud Storage. I am using RNFetchBlob, along with react-native-image-picker. I can select photos, but it will not upload. All other Firebase functions works great and it is installed through React-Native-Firebase...

Nothing seems to happen after 'fs.readFile'.

import React, { Component } from 'react'
import {
  StyleSheet,
  Text,
  View,
  TouchableOpacity,
  Platform,
  Image,
  ActivityIndicator
} from 'react-native'
import ImagePicker from 'react-native-image-picker'
import RNFetchBlob from 'rn-fetch-blob'
import firebase from 'react-native-firebase';


const storage = firebase.storage()

// Prepare Blob support
const Blob = RNFetchBlob.polyfill.Blob
const fs = RNFetchBlob.fs
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
window.Blob = Blob

const uploadImage = (uri, mime = 'application/octet-stream') => {
  return new Promise((resolve, reject) => {
    const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
    const sessionId = new Date().getTime()
    let uploadBlob = null
    const imageRef = storage.ref('images').child('${sessionId}')

    fs.readFile(uploadUri, 'base64')
      .then((data) => {
        return Blob.build(data, { type: '${mime};BASE64' })

      })
      .then((blob) => {
        uploadBlob = blob
        return imageRef.put(blob, { contentType: mime })

      })
      .then(() => {
        uploadBlob.close()
        return imageRef.getDownloadURL()
      })
      .then((url) => {
        resolve(url)
      })
      .catch((error) => {
        reject(error)
    })
  })
}


class Demo extends Component {
  constructor(props) {
    super(props)

    this.state = {}
  }

  _pickImage() {
    this.setState({ uploadURL: '' })

    ImagePicker.launchImageLibrary({}, response  => {
      uploadImage(response.uri)
        .then(url => this.setState({ uploadURL: url }))
        .catch(error => console.log(error))
    })
  }

  render() {
    return (
      <View style={ styles.container }>
        {
          (() => {
            switch (this.state.uploadURL) {
              case null:
                return null
              case '':
                return <ActivityIndicator />
              default:
                return (
                  <View>
                    <Image
                      source={{ uri: this.state.uploadURL }}
                      style={ styles.image }
                    />
                    <Text>{ this.state.uploadURL } {this.state.uploadURL}</Text>
                  </View>
                )
            }
          })()
        }
        <TouchableOpacity onPress={ () => this._pickImage() }>
          <Text style={ styles.upload }>
            Upload
          </Text>
        </TouchableOpacity>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  image: {
    height: 200,
    resizeMode: 'contain',
  },
  upload: {
    textAlign: 'center',
    color: '#333333',
    padding: 10,
    marginBottom: 5,
    borderWidth: 1,
    borderColor: 'gray'
  },
})

export default Demo

I get this error in my console when selecting a photo:

filePath.replace is not a function. (In 'filePath.replace('file://', '')', 'filePath.replace' is undefined)]


Solution

  • Just pass the image url directly to storage ref. You don't need to create a blob file anymore. I guess firebase handles it internally now.

    Here an example i just tested together with react-native-image-crop-picker:

    import firebase from 'react-native-firebase';
    import ImagePicker from 'react-native-image-crop-picker';
    
    export default class ImageUploadService {
      static init() {}
    
      static openPickerAndUploadImage() {
        const uid = '12345';
    
        ImagePicker.openPicker({
          width: 300,
          height: 300,
          cropping: true,
          mediaType: 'photo',
        })
          .then(image => {
            const imagePath = image.path;
    
            const imageRef = firebase
              .storage()
              .ref(uid)
              .child('dp.jpg');
            let mime = 'image/jpg';
    
            imageRef
              .put(imagePath, { contentType: mime })
              .then(() => {
                return imageRef.getDownloadURL();
              })
              .then(url => {
                // You could now update your users avatar for example
                //firebase.database().ref('users').child(uid).update({ ...userData})
                console.log('URL', url);
              });
          })
          .catch(error => {
            console.log(error);
          });
      }
    }
    
    ImageUploadService.init();
    

    Now just call ImageUploadService.openopenPickerAndUploadImage() in one of your components.

    I am sure this will also work with react-native-image-picker too, just remove the blob parts in your code and pass the image url directly to your imageRef.put

    ==>

    const uploadImage = (uri, mime = 'application/octet-stream') => {
      return new Promise((resolve, reject) => {
        const imagePath = uri;
    
        const imageRef = firebase
          .storage()
          .ref('images')
          .child('dp.jpg');
        let mime = 'image/jpg';
    
        imageRef
          .put(imagePath, { contentType: mime })
          .then(() => {
            return imageRef.getDownloadURL();
          })
          .then(resolve)
          .catch(reject);
      });
    };