8

I have two image paths in my component state

enter image description here

I try to upload one of the images inside of a function but get an error:

Firebase Storage: Invalid argument in 'put' at index 0: Expected Blob or file

and my function

submitImages = () => {

    // Upload images to Firebase storage
    let user = firebaseAuth.currentUser;
    let imagesRef = storageRef.child('productImages/' + user.uid);
    imagesRef.put(this.state.imageFront).then(snapshot => {
        console.log('Uploaded ' + this.state.imageFront);
    });
}

What should I be doing instead to get these images up to Firebase. Thanks!

1

6 Answers 6

14

What the error says is that you need to use a blob. You can use react-native-fetch-blob: https://github.com/wkh237/react-native-fetch-blob

Check out this example: https://github.com/dailydrip/react-native-firebase-storage/blob/master/src/App.js#L43-L69

4
  • 3
    hey, blob doesn't work in EXPO. Is there any other way?
    – chazefate
    Commented Jul 27, 2017 at 10:31
  • No use EXPO it's the only way probably? I think EXPO is limited on that part. Wondering if they have some library to help us with that. If not, just not use it. Commented Jul 27, 2017 at 19:59
  • 1
    if you use expo check this link: forums.expo.io/t/expo-and-uploading-image-blobs/227/20 Commented Aug 21, 2017 at 13:54
  • 1
    I gave up on Expo long ago. It's incompatible with half the deps I use.
    – sudo
    Commented Apr 29, 2019 at 1:37
11

I am posting my code since this was a bit frustrating for me:

To upload images to firebase.storage you need to upload the images as Blobs. If you don't know what Blobs are, don't worry: BLOB stands for Binary Large OBject.

Step 1.

npm install --save react-native-fetch-blob

Step 2.

// copy and paste this code where you will handle the file upload

import RNFetchBlob from 'react-native-fetch-blob'

const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;

Step 3.

// The uploadImage function that you are going to use:

function uploadImage(uri, mime = 'image/jpeg', name) {
  
  return new Promise((resolve, reject) => {
    let imgUri = uri; let uploadBlob = null;
    const uploadUri = Platform.OS === 'ios' ? imgUri.replace('file://', '') : imgUri;
    const { currentUser } = firebase.auth();
    const imageRef = firebase.storage().ref(`/jobs/${currentUser.uid}`)

    fs.readFile(uploadUri, 'base64')
      .then(data => {
        return Blob.build(data, { type: `${mime};BASE64` });
      })
      .then(blob => {
        uploadBlob = blob;
        return imageRef.put(blob, { contentType: mime, name: name });
      })
      .then(() => {
        uploadBlob.close()
        return imageRef.getDownloadURL();
      })
      .then(url => {
        resolve(url);
      })
      .catch(error => {
        reject(error)
    })
  })
}

So how do you call this function? Pass the URI of the image as the first argument. In my case img1, img2, img3 where variables that pointed to the URIs of the images, that I wanted to upload which were on my phone. They looked something like '/Phone/Pics/imageToUpload.jpeg', etc.

As the second argument you can pass 'image/jpeg' and the last argument is the name that you want to give the image. Chose the name that you like.

But what if I have several images and want to upload them and want to handle the upload correctly. What if one upload succeeds and the other does not?

Do this then:

let imgPromises = [];
imgPromises.push(uploadImage(img1, 'image/jpeg', 'imageOne'));
imgPromises.push(uploadImage(img2, 'image/jpeg', 'imageTwo'));
imgPromises.push(uploadImage(img3, 'image/jpeg', 'imageOne'));

Promise.all(imgPromises).then(urls => {
  // ALL IMAGES SUCCEEDED and you will get an array of URIS that you can save to your database for later use!
}).catch(error => {
  // One OR many images failed the upload. Give feedback to someone.
})
4
  • Is there a window in React Native ? Commented Feb 13, 2019 at 5:32
  • @ridoansaleh yes Commented Mar 20, 2019 at 11:03
  • How do you import?......import { window } from 'react-native'; ? When I run code I get "Cannot set property 'XMLHttpRequest' of undefined" (referring to window) Commented Nov 6, 2019 at 1:25
  • Hmm, that is odd. You don't need to import it. console.log RNFetchBlob.polyfill and check that XMLHttpRequest is defined. Commented Nov 6, 2019 at 6:41
7

You can use react-native-firebase to upload image to storge https://rnfirebase.io/

const storage = firebase.storage();
const sessionId = new Date().getTime();
const imageRef = storage.ref('images').child(`${sessionId}`);
return imageRef.putFile(uri);
7

So far this is the best method I found to upload a file/image to a Firebase Storage with React Native. This method does not use any third party libraries except for the Expo SDK.

  1. Get the File URI of the image to upload. To do this we will need to use Expo ImagePicker. The best place to include this code block is on to a button with an onPress handler.

    ImagePicker.launchImageLibraryAsync({ 
      mediaTypes: "Images"
    }).then((result)=>{ 
    
      if (!result.cancelled) {
        // User picked an image
        const {height, width, type, uri} = result;
        return uriToBlob(uri); // will follow later
      }
    
    })
    
  2. Generate a BLOB from the image URI. There are a lot of third party libraries to help do this. But if you don't want to install a library, then you can use XMLHttpRequest. The React Native docs recommends we use the Fetch API, but right now we can't use it because it will throw an error that we can only fetch https:// urls, but our URI is a file://. There is a way to get pass this, but using XMLHttpRequest will make things a lot simpler.

    uriToBlob = (uri) => {
    
    return new Promise((resolve, reject) => {
    
      const xhr = new XMLHttpRequest();
    
      xhr.onload = function() {
        // return the blob
        resolve(xhr.response);
      };
    
      xhr.onerror = function() {
        // something went wrong
        reject(new Error('uriToBlob failed'));
      };
    
      // this helps us get a blob
      xhr.responseType = 'blob';
    
      xhr.open('GET', uri, true);
      xhr.send(null);
    
    });
    
    }
    
  3. We have our BLOB, let's upload it to Firebase. This part is pretty straightforward as explained in the Firebase Docs.

    uploadToFirebase = (blob) => {
    
    return new Promise((resolve, reject)=>{
    
      var storageRef = firebase.storage().ref();
    
      storageRef.child('uploads/photo.jpg').put(blob, {
        contentType: 'image/jpeg'
      }).then((snapshot)=>{
    
        blob.close(); // let's free up the blob
    
        resolve(snapshot);
    
      }).catch((error)=>{
    
        reject(error);
    
      });
    
    });
    
    
    }
    

That's it, you can now upload a file to Firebase Storage. The key part to this is getting a File URI and converting it to a BLOB. You can read more about this method here.

1
  • 1
    Thank you for this. This is probably the best answer in this post because it doesn't use any other libraries. Just to add, if people are as stupid as I'm, don't forget that the uriToBlob function returns a promise, i.e uriToBlob(response.uri).then(blob => this.setState({photoData: blob})); or something :) Commented Nov 27, 2019 at 18:43
1

For some time I used the Firebase JS SDK with React Native. Using this library, as referred in this thread you need to use a library like rn-fetch-blob (react-native-fetch-blob is not maintained anymore) in order to provide a blob to Firebase Storage put() method.

Recently I started using React Native Firebase. As they say in their website "Using the native Firebase SDKs with React Native Firebase allows you to consume device SDKs which don't exist on the Firebase JS SDK".

Using React-Native-Firebase you don't need any extra library to upload images to Firebase Storage, and your code gets much cleaner:

export const uploadImage = (path, mime = 'application/octet-stream') => {
  return new Promise((resolve, reject) => {
    const imageRef = firebase.storage().ref('images').child('filename.jpg');

    return imageRef.put(path, { contentType: mime })
      .then(() => {
        return imageRef.getDownloadURL();
      })
      .then(url => {
        resolve(url);
      })
      .catch(error => {
        reject(error);
        console.log('Error uploading image: ', error);
      });
  });
};
0

if you don’t mind using cloudinary, I show how to upload and then get the uploaded url to save to firebase https://medium.com/@ifeoluwaking24/how-to-upload-an-image-in-expo-react-native-to-firebase-using-cloudinary-24aac981c87

Also you can try it snack but make sure you add your cloud_name and upload_preset https://snack.expo.io/@ifeking/upload-to-cloudinary

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.