firebase-js-sdk icon indicating copy to clipboard operation
firebase-js-sdk copied to clipboard

uploadBytes to storage with expo Image picker causing app to crash with no logs

Open sejaljhawer opened this issue 3 years ago • 153 comments

[REQUIRED] Describe your environment

  • Operating System version: iOS 14.8.1
  • Expo CLI 5.0.3 environment info: System: OS: macOS Sierra 10.12.6 Shell: 3.2.57 - /bin/bash Binaries: Node: 12.16.2 - /usr/local/bin/node npm: 6.14.4 - /usr/local/bin/npm SDKs: iOS SDK: Platforms: iOS 11.0, macOS 10.13, tvOS 11.0, watchOS 4.0 npmPackages: expo: ~44.0.0 => 44.0.1 react: 17.0.1 => 17.0.1 react-dom: 17.0.1 => 17.0.1 react-native: 0.64.3 => 0.64.3 react-native-web: 0.17.1 => 0.17.1 npmGlobalPackages: expo-cli: 5.0.3
  • Firebase SDK version: 9.6.1
  • Firebase Product: storage (auth, database, storage, etc)

[REQUIRED] Describe the problem

I am using Expo Image picker to select an image, convert to blob, then the Firebase 9.0 SDK function uploadBytes to add the image to my storage bucket. Around 60-80% of the time, the uploadBytes function causes my app to crash with no errors or logs recorded. When the app does not crash, the upload to Storage is successful.

Similar code and crashing symptoms as: https://stackoverflow.com/questions/70528896/react-native-expo-image-picker-upload-image-to-firebase-storage-v9-crash

Steps to reproduce:

Relevant Code:

const uploadNewPhoto = async() => {
  let result = await ImagePicker.launchImageLibraryAsync({
              mediaTypes: ImagePicker.MediaTypeOptions.Images,
              allowsEditing: false,
              // aspect: [4, 3],
              quality: 0.1,
          });
  
  if (!result.cancelled) {
              // Convert URI to a Blob via XHTML request, and actually upload it to the network
              const blob = await new Promise((resolve, reject) => {
                  const xhr = new XMLHttpRequest();
                  xhr.onload = function() {
                      resolve(xhr.response);
                  };
                  xhr.onerror = function() {
                      reject(new TypeError('Network request failed'));
                  };
                  xhr.responseType = 'blob';
                  xhr.open('GET', result.uri, true);
                  xhr.send(null);
              });
              const thisUsersNewPostRef = ref(storageRef, 'users/img1' );    
  
              uploadBytes(thisUsersNewPostRef, blob).then((snapshot) => { // causes crash
                  console.log('Uploaded a blob or file!');
              });
  }
}


sejaljhawer avatar Jan 02 '22 23:01 sejaljhawer

Hello! In order to solve your issue, I first recommend that you update to the latest version of firebase (v9) within your Expo application. Then, watch this video: https://www.youtube.com/watch?v=H-yXO46WDak . Very recent video so it incorporates version 9 and also should solve your issue because it also tries to upload image to firebase storage. Hope this helps!

Eesha-Jain avatar Jan 06 '22 04:01 Eesha-Jain

When looking at your code, I believe that it is the method that you use to generate the blob is complicated and prone to errors. Reference the video I just sent to see how I recommend you turn your image into a blob.

Eesha-Jain avatar Jan 06 '22 04:01 Eesha-Jain

@Eesha-Jain Your YouTube code works for images through ImagePicker but not for videos. It always crashes on iOS videos. Android videos go through just fine.

rgioeli avatar Jan 06 '22 06:01 rgioeli

Ohh, alright. Looks like this may hold a solution: https://github.com/expo/expo/issues/3177

Eesha-Jain avatar Jan 06 '22 07:01 Eesha-Jain

@rgioeli hey, I am having the same issue - did you end up finding a solution that worked for you?

bricee98 avatar Jan 19 '22 08:01 bricee98

@rgioeli hey, I am having the same issue - did you end up finding a solution that worked for you?

I downgraded to v8 of firebase sdk, that worked for me. Not sure if it's an option for you, but maybe for other people reading this.

As described in the comment at https://www.youtube.com/watch?v=H-yXO46WDak&lc=UgwNOcXKFQl2UARHz6Z4AaABAg.9Wqk13KYbzW9WuNC4HWz_s

pfibbe avatar Jan 19 '22 08:01 pfibbe

@pfibbe I just saw that mentioned somewhere else as well - seems like the way to go. Thanks!

bricee98 avatar Jan 19 '22 09:01 bricee98

Thanks for the info that this works with 8.10.0 and not in 9.6.1. Would anybody be able to try and see if this is also broken in any earlier v9 builds, such as 9.2.0 perhaps? It would help us narrow down when the relevant change happened.

hsubox76 avatar Jan 19 '22 18:01 hsubox76

I was actually using 9.2.0 when I came across the issue.

bricee98 avatar Jan 19 '22 23:01 bricee98

Does the error also occur in 9.0.0?

hsubox76 avatar Jan 20 '22 17:01 hsubox76

Unsure - unfortunately, I won't be able to check for a while, sorry.

bricee98 avatar Jan 21 '22 01:01 bricee98

I would recommend going to 8.10.0 cause we're sure that works

Eesha-Jain avatar Jan 21 '22 02:01 Eesha-Jain

However, trying won't do any harm! :)

Eesha-Jain avatar Jan 21 '22 02:01 Eesha-Jain

Same issue, some video files fail randomly.

jackblackCH avatar Jan 24 '22 00:01 jackblackCH

If anyone can confirm the error also occurs in 9.0.0 and does not occur in 8.10.0, it will help us a lot in narrowing down the change that caused it.

hsubox76 avatar Jan 24 '22 17:01 hsubox76

I will test it now with specific version of 9.0.0. It was definitely not working with ^9.2.0, then I switched back to 8.10.0 as mentioned here) and it was not working either. That being said, only with certain videos. Probably, as mentioned coz of memory issues with not small videos.

If anyone can confirm the error also occurs in 9.0.0 and does not occur in 8.10.0, it will help us a lot in narrowing down the change that caused it.

jackblackCH avatar Jan 25 '22 22:01 jackblackCH

Hello, everyone i also faced the same issue, on firebase version 9.6.5, so i have used function uploadBytesResumable instead of uploadBytes, it works just fine, if it is urgent you can use that function for the time being, just a recommendation, for reference:- https://firebase.google.com/docs/storage/web/upload-files

I faced this issue in iOS 15 and 15 + only, rest below iOS 15 and in android OS uploadBytes is working fine.

Note:- when using XMLHttpRequest with uploadBytesResumable, first time image uploads smoothly on iOS 15.2 then, on uploading it on second time there is crash while uploading image. To avoid it use, const img = await fetch(image_url); const blob = await img.blob();

this way we can also get blob file;

import { getStorage, ref, getDownloadURL } from "firebase/storage";

const storageRef = ref(getStorage(), "image_name");

const img = await fetch(image_url); const blob = await img.blob();

console.log("uploading image"); const uploadTask = uploadBytesResumable(storageRef, blob);

// Listen for state changes, errors, and completion of the upload. uploadTask.on('state_changed',(snapshot) => { // 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 'paused': console.log('Upload is paused'); break; case 'running': console.log('Upload is running'); break; } }, (error) => { this.setState({ isLoading: false }) // A full list of error codes is available at // https://firebase.google.com/docs/storage/web/handle-errors switch (error.code) { case 'storage/unauthorized': console.log("User doesn't have permission to access the object"); break; case 'storage/canceled': console.log("User canceled the upload"); break; case 'storage/unknown': console.log("Unknown error occurred, inspect error.serverResponse"); break; } }, () => { // Upload completed successfully, now we can get the download URL getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => { console.log('File available at', downloadURL); //perform your task }); });

pranav-singh-parmar avatar Jan 28 '22 18:01 pranav-singh-parmar

If anyone can confirm the error also occurs in 9.0.0 and does not occur in 8.10.0, it will help us a lot in narrowing down the change that caused it.

well i was using v9.4 then i upgrade it to latest version. i was able to upload file less than 2mb ... greater files will make the app crash

so i went down to version 9.0.0 and i was able to upload files at 100mb+ so i think this issue related to newer versions of firebase.

salmanExpo avatar Jan 30 '22 20:01 salmanExpo

Will keep trying to dig into this but so far it seems that this error:

  1. affects larger files
  2. can be avoided by using uploadBytesResumable() instead of uploadBytes()
  3. can also be avoided by using fetch() instead of XMLHttpRequest to get the image from the Expo image picker url

It seems 2 and 3 may be viable workarounds for the time being if anyone is blocked.

As far as more info about versions affected, it seems to affect:

  1. iOS 15+
  2. Firebase JS SDK 9.2.0 ?? +

Feel free to chime in with any corrections, updates, or other workarounds. We will try to keep looking for what might have caused this in the Firebase SDK.

hsubox76 avatar Feb 01 '22 19:02 hsubox76

I am experiencing this issue as well.

  1. This is in an expo managed app.
  2. The error only appears on videos longer than about 10 seconds @ 720p.
  3. I am using fetch to retrieve the blob before upload.
  4. I experience it both with uploadBytesResumable where it crashes without error after a few seconds. And uploadBytes where it crashes immediately.

I have pinpointed that I can rollback to Firebase JS SDK 9.3.0 and everything works as expected, but upgrading to 9.4.1 - 9.6.6 causes the failure to occur. So for our experience it's something introduced between 9.3.0 and 9.4.1.

GYatesIII avatar Feb 09 '22 02:02 GYatesIII

The code that i wrote above is running fine on my mac but when complied from my colleague's pc created the same issue. We were still not able to resolve it by downgrading firebase version. I don't think issue lies in firebase sdk. It's just that it is not compatible with iOS 15 ?? + (because i only faced this issue in iOS 15). Maybe a support for iOS 15 needs to be added as iOS 15 has come up with major updations. Just a guess. Try to solve issue by changing firebase version and maybe compiling your code on other device. I will still recommend to use uploadResumable bytes and fetch.

pranav-singh-parmar avatar Feb 09 '22 15:02 pranav-singh-parmar

How would i rollback to a different version of FIREBASE?

juliancstrocodes avatar Feb 10 '22 03:02 juliancstrocodes

How would i rollback to a different version of FIREBASE? you will need to use name and version of the package npm install [email protected]

salmanExpo avatar Feb 10 '22 07:02 salmanExpo

async function uploadImageAsync(uri, title, index) {
   // Why are we using XMLHttpRequest? See:
   // https://github.com/expo/expo/issues/2402#issuecomment-443726662
   const blob = await new Promise((resolve, reject) => {
     const xhr = new XMLHttpRequest();
     xhr.onload = function () {
       resolve(xhr.response);
     };
     xhr.onerror = function (e) {
       reject(new TypeError("Network request failed"));
     };
     xhr.responseType = "blob";
     xhr.open("GET", uri, true);
     xhr.send(null);
   });

   const fileRef = ref(storage, `images/events/${title}/image${index}`);
   const result = await uploadBytesResumable(fileRef, blob);

   // We're done with the blob, close and release it
   blob.close();

   return await getDownloadURL(fileRef);
 }

Been using the above code to upload images to firebase (version 9.6.6). Works perfectly with one image, but cannot upload multiple images. Any suggestions? Have tried using await and .then() to try looping, but that has not worked either.

SRoche14 avatar Feb 10 '22 16:02 SRoche14

So we have one report that the bug occurred in 9.2.0, and a separate report that it did not occur in 9.3.0 but did occur in 9.4.1, which seems confusing to me. Any additional data about what versions the error is or isn't happening would be helpful in sorting out what changes to look at.

hsubox76 avatar Feb 11 '22 00:02 hsubox76

async function uploadImageAsync(uri, title, index) {
   // Why are we using XMLHttpRequest? See:
   // https://github.com/expo/expo/issues/2402#issuecomment-443726662
   const blob = await new Promise((resolve, reject) => {
     const xhr = new XMLHttpRequest();
     xhr.onload = function () {
       resolve(xhr.response);
     };
     xhr.onerror = function (e) {
       reject(new TypeError("Network request failed"));
     };
     xhr.responseType = "blob";
     xhr.open("GET", uri, true);
     xhr.send(null);
   });

   const fileRef = ref(storage, `images/events/${title}/image${index}`);
   const result = await uploadBytesResumable(fileRef, blob);

   // We're done with the blob, close and release it
   blob.close();

   return await getDownloadURL(fileRef);
 }

Been using the above code to upload images to firebase (version 9.6.6). Works perfectly with one image, but cannot upload multiple images. Any suggestions? Have tried using await and .then() to try looping, but that has not worked either.

Use this code to get blob file instead of XMLHttpRequest const img = await fetch(uri); const blob = await img.blob();

pranav-singh-parmar avatar Feb 11 '22 03:02 pranav-singh-parmar

So we have one report that the bug occurred in 9.2.0, and a separate report that it did not occur in 9.3.0 but did occur in 9.4.1, which seems confusing to me. Any additional data about what versions the error is or isn't happening would be helpful in sorting out what changes to look at.

I can confirm that I stepped through minor versions one-by-one until it started occurring and it first started in 9.4

This is on managed Expo app served through the Expo app. The impact on iOS it uploads for a bit then crashes without error. On Android, it uploads for a bit then hangs forever without error. This testing was done on real devices, not simulators.

I only stepped through the individual versions within the Expo app but can also verify it worked in 9.0 when deployed through the official stores, and was broken in 9.6. On both platforms.

GYatesIII avatar Feb 11 '22 21:02 GYatesIII

Hello, I'm having the same issue, but I might be able to add some insight. Here is my current takePicture function:

const takePicture = async () => { if (cameraRef.current) { const options = { quality: 0.1, base64: true, skipProcessing: true }; const data = await cameraRef.current.takePictureAsync(options); const source = data.uri if (source) { navigateToSave(source) } } };

I'm also using the Full Example from the Firebase Upload Files documentation.

With the current options, I am able to upload both images from the camera to Firebase. However, if I remove the quality option, the upload fails 100% of the time. It would seem that the size of the file might cause uploadBytesResumable to crash.

Using Firebase v9.6.6 and Expo v43.0.0; Windows 10 and Expo Go.

cpon00 avatar Feb 15 '22 08:02 cpon00

There's 2 reports indicating this may have started in 9.4.1, and if so, this would be the relevant commit: https://github.com/firebase/firebase-js-sdk/pull/5703 Will try to take a closer look.

hsubox76 avatar Feb 18 '22 17:02 hsubox76

There's 2 reports indicating this may have started in 9.4.1, and if so, this would be the relevant commit: #5703 Will try to take a closer look.

I can confirm that I have the problem with 9.2.0 and above (including 9.3.0). I haven't been able to test yet with 9.0.0 and 9.1.0.

Cosmicoda avatar Feb 18 '22 20:02 Cosmicoda