Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
sejaljhawer opened this issue Jan 2, 2022 · 155 comments

Comments

@sejaljhawer
Copy link

sejaljhawer commented Jan 2, 2022

[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!');
              });
  }
}
@Eesha-Jain
Copy link

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
Copy link

Eesha-Jain commented Jan 6, 2022

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.

@rgioeli
Copy link

rgioeli commented Jan 6, 2022

@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.

@Eesha-Jain
Copy link

Ohh, alright. Looks like this may hold a solution: expo/expo#3177

@bricee98
Copy link

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

@pfibbe
Copy link

pfibbe commented Jan 19, 2022

@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

@bricee98
Copy link

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

@hsubox76
Copy link
Contributor

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.

@bricee98
Copy link

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

@hsubox76
Copy link
Contributor

Does the error also occur in 9.0.0?

@bricee98
Copy link

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

@Eesha-Jain
Copy link

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

@Eesha-Jain
Copy link

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

@jackblackCH
Copy link

Same issue, some video files fail randomly.

@hsubox76
Copy link
Contributor

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
Copy link

jackblackCH commented Jan 25, 2022

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.

@pranav-singh-parmar
Copy link

pranav-singh-parmar commented Jan 28, 2022

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
});
});

@salmanExpo
Copy link

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.

@hsubox76
Copy link
Contributor

hsubox76 commented Feb 1, 2022

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.

@GYatesIII
Copy link

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.

@pranav-singh-parmar
Copy link

pranav-singh-parmar commented Feb 9, 2022

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.

@juliancstrocodes
Copy link

How would i rollback to a different version of FIREBASE?

@salmanExpo
Copy link

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]

@SRoche14
Copy link

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.

@hsubox76
Copy link
Contributor

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.

@pranav-singh-parmar
Copy link

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();

@GYatesIII
Copy link

GYatesIII commented Feb 11, 2022

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.

@cpon00
Copy link

cpon00 commented Feb 15, 2022

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.

@mike-javascript
Copy link

Is this error, which, as far as I understand, is due to Expo and the file size, only occurring in development, or does it not work in production either?

@hsubox76
Copy link
Contributor

hsubox76 commented May 3, 2023

@ZackryPaul Thank you so much for this investigation. I and @maneesht have poured a lot of time into trying to debug this to no avail. I especially appreciate you discovering that this also happens in react-native-firebase which indicates that this isn't a bug in the JS SDK.

I moved to react-native-firebase and the crash would still happen. Changed Expo SDKs, RN SDKs, and everything else people would suggest and nada.

Eventually, I realized, the problem is likely in the file type and provided metadata. For iOS the app would crash if the image wasn't loaded on the device (a cloud photo). Android would upload far more frequently than iOS but would also have an occasional crash.

Finally, after trying to work with what I could, I used Expo manipulateAsync to save images as png and add file type in the metadata before upload and without fault, images uploaded every time whether they were physically on the device or in the cloud.

Even though it seems the JS SDK isn't causing this, we can try to look further into your solution to see if our code can fill in the metadata in a way that prevents the problem. Not sure off the top of my head how to do it, since we obviously can't force every image to be converted to PNG, but maybe if it's a missing/incorrect metadata issue we can figure out how to fill that in.

@ZackryPaul
Copy link

Yes, I definitely don't think it's firebase itself causing this issue. After these changes, I haven't had a problem yet! I hope you guys get this worked out!

@mike-javascript
Copy link

but no one has put it into production yet to see if it works ?

@hsubox76
Copy link
Contributor

hsubox76 commented May 3, 2023

I don't think there should be any difference between dev and production? Are you seeing a difference? If so can you elaborate on your project setup and what the difference is between dev and production? I think you're the first person to mention a difference so it would be great if you could provide the details.

@mike-javascript
Copy link

I don't know, I'm just asking the question. I'm in the middle of development and I thought I understood that it was due to Expo, so I was asking if anyone else had the same problem in production and how people manage to make it work.

@delandchen
Copy link

delandchen commented Sep 29, 2023

I also tried many different things to work around this and eventually came across this issue which may be related: facebook/react-native#27099

So I did the following workaround to get the blob correctly:

const getImageBlob = async (uri: string) => {
  const originalUri = uri;
  const fileName = uri.substring(uri.lastIndexOf('/') + 1);
  // Workaround see https://github.com/facebook/react-native/issues/27099
  const newUri = `${FileSystem.documentDirectory}resumableUploadManager-${fileName}.toupload`;
  await FileSystem.copyAsync({ from: originalUri, to: newUri });
  const response = await fetch(newUri);
  const blobData = await response.blob();
  return new Blob([blobData], { type: 'image/jpeg' });
};

Additionally, I also check the file size of the image and compress it even more with expo-file-system if the size is larger than 4 MB.

I'm not sure what exactly helped in the end, but with the following code, I wasn't having any issues for quite some time now.

const uploadFile = async (uri: string, pathReference: string): Promise<string> => {
  let imageBlob: any;
  if (await sizeIsLessThanMB(uri, MAX_FILE_SIZE_MB)) {
    imageBlob = await getImageBlob(uri);
  } else {
    const imageResult = await compressImage(uri);
    const smallerThanMaxSize = await sizeIsLessThanMB(imageResult.uri, MAX_FILE_SIZE_MB);
    if (!smallerThanMaxSize) {
      throw new Error('image-too-large');
    }
    imageBlob = await getImageBlob(imageResult.uri);
  }

  const path = pathReference ? `${pathReference}/${uuidv4()}` : uuidv4();
  const storageRef = ref(storage, path);
  const snapshot = await uploadBytesResumable(storageRef, imageBlob);
  return getDownloadURL(snapshot.ref);
};

Maybe this helps someone.

This solution worked best for me. Check and compress if the size is larger than 1mb + uploadBytesResumable. Also the workaround for this https://github.com/facebook/react-native/issues/27099 issue was crucial.

Filesize of the images weirdly increased after fetching the blob and uploading it to Firebase Storage, so the compression essentially did nothing if I didn't use this workaround.

@Fgabz
Copy link

Fgabz commented Oct 17, 2023

Is there a solution without compressing that much pictures?

@JacobJaffe
Copy link

JacobJaffe commented Oct 17, 2023

I'd recommend that people don't use uploadBytesResumable / uploadBytes, if only because you can't make it work as a background task. If you're making an app that may incur long uploads, you'll probably want them working in the background. Within Expo, that means using FileSystem.uploadAsync

Instead, I'd recommend using a small wrapper in a firebase function around cloud storage, and having the app treat that endpoint like a normal http upload. This is similar to what people are talking about with Cloudinary and other upload options, but doesn't require migrating ecosystems... just building one small endpoint on a Firebase cloud funciton.

Other nice benefits include being able to run more robust validation through a function, and being able to perform image processing (thumbnail generation, blurhash generation, etc.) within the upload endpoint, rather than in a side effect.

I wrote up a more in-depth solution on StackOverflow for this: https://stackoverflow.com/a/72694900/19379023

Bumping this, since people still encounter this issue.
I would highly recommend people newly encountering this issue to consider using a Firebase Function to create an http endpoint you upload to via Expo's FileSystem.uploadAsync().

This solution:

  • Works without compressing the image
  • Works when the app is backgrounded
  • Uploads faster (and probably performs better for parallel uploads, since more occurs on the native side. I haven't benchmarked this)
  • Allows server-side control of image validation and processing, before or after the image is added to storage

My app has been using this strategy for a year with thousands of uploaded photos. It just requires a few hours to set this up, and you can stay 100% within Expo and Firebase ecosystems.

@geoffcfchen
Copy link

I have been facing this issue for one year, and the problems keep on and off. I would really recommend that just switch to using React Native Firebase. Although it takes a bit more time to set it up, but it will totally save you tons of time.

@ben-hamel
Copy link

I have been facing this issue for one year, and the problems keep on and off. I would really recommend that just switch to using React Native Firebase. Although it takes a bit more time to set it up, but it will totally save you tons of time.

@geoffcfchen So you haven't been running into any issues with React Native Firebase? ZackryPaul commented above that he was still having issues with either Expo or RNF so I never bothered to try the switch.

If anyone wants a repo example ZacharyPauls suggestion, you can check my project here- https://github.com/ben-hamel/rn-the-scene-app-expo-version

The main difference with mine was having to restrict down to 1MB max instead of 4MB and I set the width when compressing the image, which allowed me to keep compression at 0.9 so far.

@geoffcfchen
Copy link

geoffcfchen commented Oct 18, 2023

I have been facing this issue for one year, and the problems keep on and off. I would really recommend that just switch to using React Native Firebase. Although it takes a bit more time to set it up, but it will totally save you tons of time.

@geoffcfchen So you haven't been running into any issues with React Native Firebase? ZackryPaul commented above that he was still having issues with either Expo or RNF so I never bothered to try the switch.

If anyone wants a repo example ZacharyPauls suggestion, you can check my project here- https://github.com/ben-hamel/rn-the-scene-app-expo-version

The main difference with mine was having to restrict down to 1MB max instead of 4MB and I set the width when compressing the image, which allowed me to keep compression at 0.9 so far.

No, I haven't faced any issues since I switched to react-native-firebase. I feel that it is much more stable than Firebase JS SDK, but maybe I haven't tested all the functions thoroughly.

@nickjuntilla
Copy link

I'd recommend that people don't use uploadBytesResumable / uploadBytes, if only because you can't make it work as a background task. If you're making an app that may incur long uploads, you'll probably want them working in the background. Within Expo, that means using FileSystem.uploadAsync
Instead, I'd recommend using a small wrapper in a firebase function around cloud storage, and having the app treat that endpoint like a normal http upload. This is similar to what people are talking about with Cloudinary and other upload options, but doesn't require migrating ecosystems... just building one small endpoint on a Firebase cloud funciton.
Other nice benefits include being able to run more robust validation through a function, and being able to perform image processing (thumbnail generation, blurhash generation, etc.) within the upload endpoint, rather than in a side effect.
I wrote up a more in-depth solution on StackOverflow for this: https://stackoverflow.com/a/72694900/19379023

Bumping this, since people still encounter this issue. I would highly recommend people newly encountering this issue to consider using a Firebase Function to create an http endpoint you upload to via Expo's FileSystem.uploadAsync().

This solution:

  • Works without compressing the image
  • Works when the app is backgrounded
  • Uploads faster (and probably performs better for parallel uploads, since more occurs on the native side. I haven't benchmarked this)
  • Allows server-side control of image validation and processing, before or after the image is added to storage

My app has been using this strategy for a year with thousands of uploaded photos. It just requires a few hours to set this up, and you can stay 100% within Expo and Firebase ecosystems.

The problem with doing it this way is that Firebase functions limit the amount of data you can send through http. I've been doing it this way for years and have always been limited to 10mb. More recently Gen2 is supposed to limit you to 32mb, but that is still a pretty huge limitation. We really need direct upload to work.
Firebase HTTPS Size Restrictions

@JacobJaffe
Copy link

@nickjuntilla That's a good point for video or other large format uploads. However, most people are coming to this thread to figure out a solution to image uploading, and going with solutions that involve compressing to 1MB.

32MB uploads with background availability is a huge win for that majority use case.

@nickjuntilla
Copy link

nickjuntilla commented Nov 14, 2023

@nickjuntilla That's a good point for video or other large format uploads. However, most people are coming to this thread to figure out a solution to image uploading, and going with solutions that involve compressing to 1MB.

32MB uploads with background availability is a huge win for that majority use case.

Well it's good for people to understand the limitation either way. In any case people may also be pleased to know that as of firebase 10.1.0 this code works as is described in the video above:

const image = await fetch(localPath);
  const bytes = await image.blob();
  uploadBytesResumable(storageRef, bytes, metadata).then((snapshot) => {
    console.log(
      "Uploaded a blob or file!",
      JSON.stringify(snapshot, null, 4)
    );
  });

I came here because using the bytes64 returned from the imagePicker was not working, but it appears firebase has fixed the upload issue as long as you covert it to a blob first. This will be more simple for a lot of people.

Also I think it's important that people understand the 10mb/32mb http pipeline limits in firebase because it's an extremely hard issue to figure out if you aren't aware of it. There is no proper error message, it will just fail. It's not the server RAM, it's not busboy limits, it's not express, it's firebase functions. Maybe I'll save someone a month on that.

@mrrqzhang
Copy link

I had the issues but fixed by adding timeout(1000) after "await response.blob()". I can't figure out why. It seems to me blob not completed even if "await response.blob()" returned.

@XkoderFX
Copy link

XkoderFX commented Jun 8, 2024

if anyone face this issue on firebase: 10.12.2, the upload finally cease crashing the app when I switched to uploadBytesResumable and defined the format.

const d = await cameraRef.current?.takePictureAsync({
        base64: true,
        skipProcessing: false,
      });
      if (!d) {
        return;
      }
      const storage = getStorage(firebaseApp);
      const storageRef = ref(storage, "avatars/4.png");
      const res = await fetch(d.uri);

      const blob = await res.blob();

      uploadBytesResumable(storageRef, blob).then((sanpshoot) => {
        console.log("uploaded");
      });

@aboveStars
Copy link

Temporarily, saving image before creating Blob solves problem.

I am using below code to create Blob:

import { SaveFormat, manipulateAsync } from "expo-image-manipulator";

export default async function createBlobFromURI(uri: string): Promise<Blob> {
  const localUri = await createLocalFile(uri);

  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response);
      } else {
        reject(new Error(`Failed to fetch resource: ${xhr.statusText}`));
      }
    };
    xhr.onerror = (e) => {
      console.error("Failed to create blob from URI", e);
      reject(new Error("Network error"));
    };
    xhr.responseType = "blob";
    xhr.open("GET", localUri, true);
    xhr.send();
  });
}

export async function createLocalFile(uri: string) {
  const result = await manipulateAsync(uri, [], {
    format: SaveFormat.JPEG,
  });

  return result.uri;
}

Also, my Image Picker setup is like below:

await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      allowsEditing: true,
      aspect: [1, 1],
      quality: 0.2,
    });

And finally, I am sending image to Storage like below:
(Don't forget to add ".jpg" extension to the end of filename)

const blob = await createBlobFromURI(image);
const path = `vegetables/carrot.jpg`;
const storageRef = ref(storage, path);

const uploadTask = uploadBytesResumable(storageRef, blob, {
        contentType: "image/jpeg",
});

@scaryguy
Copy link

scaryguy commented Jul 10, 2024

In my case, it's not an image file but it's a m3u8 file that I'm trying to upload to to Cloud Storage.

const m3u8 = new Blob([STRING_VALUE], {
  type: "audio/mpegurl",
});
const masterM3u8Ref = ref(storage, `my_m3u8.m3u8`);
await uploadBytesResumable(masterM3u8Ref, newMasterBlob, metadata);

Just like described above for image cases; sometimes it works and mostly it doesn't... App silently crashes without any error. I can't believe that this issue has been lingering since more than 2 years now... How people can rely on firebase-js-sdk in their react-native apps while this kind of issues stay open for years?..

Anyway; after hours and hours of investigation and research online; finally I applied "saving the file to the filesystem and read from there" strategy and got it working.

const m3u8 = new Blob([STRING_VALUE], {
  type: "audio/mpegurl",
});
// Disgusting workaround: https://github.com/firebase/firebase-js-sdk/issues/5848
const outurl = URL.createObjectURL(m3u8);
const masterM3u8Blob = await (await fetch(outurl)).blob()
const masterM3u8Ref = ref(storage, `my_m3u8.m3u8`);
await uploadBytesResumable(masterM3u8Ref, newMasterBlob, metadata);

Now it works, but to be honest, I hate it.

@edi
Copy link

edi commented Jul 10, 2024

I can't believe that this issue has been lingering since more than 2 years now... How people can rely on firebase-js-sdk in their react-native apps while this kind of issues stay open for years?..

Well, as you mentioned, it’s the JS SDK while you’re supposed to use RNFirebase for react-native projects. After a lot of debate. It doesn’t necessarily look like this is an issue with the JS SDK itself (as it doesn’t crash on the web for which it was made), but more like an incompatibility between RN and Firebase JS SDK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests