Search code examples
javascriptreact-nativeblobreact-native-camerareact-native-fetch-blob

Upload image taken with camera to firebase storage on React Native


All I want to do is upload a photo taken using react-native-camera to firebase storage with react-native-fetch-blob, but no matter what I do it doesn't happen.

I've gone through all of the documentations I can find and nothing seems to work.

If anyone has a working system for accomplishing this please post it as an answer. I can get the uri of the jpg that react-native-camera returns (it displays in the ImageView and everything), but my upload function seems to stop working when it's time to put the blob.

My current function:

uploadImage = (uri, imageName, mime = 'image/jpg') => {
    return new Promise((resolve, reject) => {
      const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
        let uploadBlob = null
        const imageRef = firebase.storage().ref('selfies').child(imageName)
        console.log("uploadUri",uploadUri)
        fs.readFile(uploadUri, 'base64').then((data) => {
          console.log("MADE DATA")
          var blobEvent = new Blob(data, 'image/jpg;base64')
          var blob = null
          blobEvent.onCreated(genBlob => {
            console.log("CREATED BLOB EVENT")
            blob = genBlob
            firebase.storage().ref('selfies').child(imageName).put(blob).then(function(snapshot) {
              console.log('Uploaded a blob or file!');
              firebase.database().ref("selfies/" + firebase.auth().currentUser.uid).set(0)
              var updates = {};
              updates["/users/" + firebase.auth().currentUser.uid + "/signup/"] = 1;
              firebase.database().ref().update(updates);
            });
          }, (error) => {
            console.log('Upload Error: ' + error)
            alert(error)
          }, () => {
            console.log('Completed upload: ' + uploadTask.snapshot.downloadURL)
          })
        })
      }).catch((error) => {
          alert(error)
      })
  }

I want to be as efficient as possible, so if it's faster and takes less memory to not change it to base64, then I prefer that. Right now I just have no clue how to make this work.

This has been a huge source of stress in my life and I hope someone has this figured out.


Solution

  • The fastest approach would be to use the native android / ios sdk's and avoid clogging the JS thread, there are a few libraries out there that will provide a react native module to do just this (they all have a small js api that communicates over react natives bridge to the native side where all the firebase logic runs)

    react-native-firebase is one such library. It follows the firebase web sdk's api, so if you know how to use the web sdk then you should be able to use the exact same logic with this module as well as additional firebase apis that are only available on the native SDKS.

    For example, it has a storage implementation included and a handy putFile function, which you can provide it with a path to a file on the device and it'll upload it for you using the native firebase sdks, no file handling is done on the JS thread and is therefore extremely fast.

    Example:

    // other built in paths here: https://github.com/invertase/react-native-firebase/blob/master/lib/modules/storage/index.js#L146
    const imagePath = firebase.storage.Native.DOCUMENT_DIRECTORY_PATH + '/myface.png';
    const ref = firebase.storage().ref('selfies').child('/myface.png');
    
    const uploadTask = ref.putFile(imagePath);
    
    // .on observer is completely optional (can instead use .then/.catch), but allows you to
    // do things like a progress bar for example
    uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => {
      // observe state change events such as progress
      // get task progress, including the number of bytes uploaded and the total number of    bytes to be uploaded
      const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
      console.log(`Upload is ${progress}% done`);
    
      switch (snapshot.state) {
        case firebase.storage.TaskState.SUCCESS: // or 'success'
          console.log('Upload is complete');
          break;
        case firebase.storage.TaskState.RUNNING: // or 'running'
          console.log('Upload is running');
          break;
        default:
          console.log(snapshot.state);
      }
    }, (error) => {
      console.error(error);
    }, () => {
      const uploadTaskSnapshot = uploadTask.snapshot;
      // task finished
      // states: https://github.com/invertase/react-native-firebase/blob/master/lib/modules/storage/index.js#L139
      console.log(uploadTaskSnapshot.state === firebase.storage.TaskState.SUCCESS);
      console.log(uploadTaskSnapshot.bytesTransferred === uploadTaskSnapshot.totalBytes);
      console.log(uploadTaskSnapshot.metadata);
      console.log(uploadTaskSnapshot.downloadUrl)
    });

    Disclaimer: I am the author of react-native-firebase.