blob stopped working in expo sdk 39

I am also having problems with blob in iOS with sdk 39, in android it works perfectly and in later versions.
When I want to upload a file to firebase storage it uploads it but with application/octet-stream format and 0 byte.

export const urlToBlob = (url) => {
  return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      xhr.onerror = reject;
      xhr.onreadystatechange = () => {
          if (xhr.readyState === 4) {
              resolve(xhr.response);
          }
      };
      xhr.open('GET', url);
      xhr.responseType = 'blob';
      xhr.send();
  })
}
1 Like

I also have this problem at iOS

this works as expected, as far as i can tell: examples/with-firebase-storage-upload at master · expo/examples · GitHub. you can run this on your machine by running npx crna --template with-firebase-storage-upload

if you can provide a runnable example like the above and create an issue we can investigate. i don’t see any issues like this on the react-native repository, and that is where the fetch and blob code lives, so it seems possible that this is an issue with your app code, but it’s impossible to tell without having a mcve (How to create a Minimal, Reproducible Example - Help Center - Stack Overflow.

you may also want to consider FileSystem - Expo Documentation as an alternative to using XMLHttpRequest. it also works for uploading files when the app is backgrounded.

I have the same issue. Used to work just fine before upgrading the SDK to version 39.

// uri's value = file:///var/mobile/Containers/Data/Application/D791BFAD-6478-4780-8BDE-AEFA27A41D75/Library/Caches/ExponentExperienceData/%2540eduardjs%252FBySpark/ImageManipulator/2E2B36BA-EEED-414E-8AFE-7264A4773AB9.jpg
let file = await fetch(uri)

So, the image exists locally, as it successfully renders in an <Image /> just that when trying to fetch it, it suddenly returns this error.

Trying to figure out how to fix … tried with different codes for fetching the blob, tried using Axios, then using plain XHR (like in the OP’s post).

Also tried with this. Gives the same error as the OP’s on both iOS / Android, locally as well as within the builds for the simulators.

So far, no luck. Can’t think of anything else to fix this … please advise.

For some reason I couldn’t edit my previous post. I’ve just tried installing the example storage upload. Works fine on the first try, but then, if you try uploading for a second time, the error occurs, hence, error is somewhere within v39.

I noticed this as well. I got a work around working for the time being: Apparently you can still access the values via the FileSystem then you can get the file as base64 and then create a buffer from the base64 encoded value and send that as a blob. Here’s the code I used.

import { Buffer } from "buffer"; // get this via: npm install buffer
import * as FileSystem from "expo-file-system";

const path = "some/path/to/the/selected/image.png";

const result = await ImageManipulator.manipulateAsync(
  path,
  [{ resize: { width: 300 } }],
   {
    compress: 0.5,
    format: ImageManipulator.SaveFormat.JPEG,
  },
);
const options = { encoding: FileSystem.EncodingType.Base64 };
const base64Response = await FileSystem.readAsStringAsync(
    results.uri,
    options,
);

const blob = Buffer.from(base64Response, "base64");
// upload blob

I know this isn’t a “fix” for the actual problem but this is a work around until the bug is fixed.

3 Likes

We have been having the same issues. Tried everything from fetching the file to building something similar to buffer. We’re building a functionality where a user can choose a photo from their phone which then is uploaded to Microsoft Graph which expects a binary.

When using a fetch request the response returns an image, but the .blob() method seems be undefined on fetch responses which I believe should have been available since SDK 35.

A get through an XHR request does return a blob, but like someone before stated with 0 bytes.

Buffer was the final piece for our fix, thanks!

Thats exactly the code that I used, but since I upload the SDK to 39 it sent me the error. here is my complete function:

async function uploadImageAsync(uri) {
  // 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) {
      console.log(e);
      reject(new TypeError('Network request failed'));
    };
    xhr.responseType = 'blob';
    xhr.open('GET', uri, true);
    xhr.send(null);
  });

  const ref = firebase
    .storage()
    .ref()
    .child(uuid.v4());
  const snapshot = await ref.put(blob);
  const profileImage = await snapshot.ref.getDownloadURL();
  console.log(profileImage)
  // We're done with the blob, close and release it
  blob.close();
  return await snapshot.ref.getDownloadURL();
}
1 Like

Do you have your upload function?

Using davidbrear Buffer tip we got firebase storage uploads working [with-firebase-storage-upload] : fails after uploading one picture. · Issue #221 · expo/examples · GitHub

1 Like

hi all! in my testing, this commit from react-native fixes it: Remove requestToken being nil check from [RCTNetworkTask validateRequ… · facebook/react-native@2c5d4cb · GitHub. if you want to confirm this on your end, eject your app and run it with react-native 0.63.3 to see if you encounter the issue. if that fixes it, try running it again on 0.63.2, you should see the issue again. if you can do this it will be helpful to know that this commit fixes the specific issue that you are facing which could be different. i verified this using examples/with-firebase-storage-upload at master · expo/examples · GitHub using these steps.

we will cherry-pick this in to our next minor release.

Ejected the affected app we are working by following the eject steps (first time I have done this) performed tests in react-native 0.63.3 and 0.63.2.

I can confirm for us that in 0.63.3 the problem is fixed and XMLHttpRequest works as expected

Thank you

thank you @simoncar!

So we have to wait for EXPO to work with this react-native version?
or we have to eject our Proyects?

as i said above we plan to do a patch release to include this fix, we’ll try to get this ready this week or next. until then you can use sdk 38 or lower, or eject if you must use sdk 39.

Perfect thks a lot @notbrent

Just in case someone needs a workaround to get images uploaded to firebase storage AND uses the image resize extension. My problem was that firebase storage complained about “unsupported image format” and therefore couldn’t resize the images (create thumbnails) automatically.

import { atob } from 'abab' // react native has no built in atob()/btoa() methods, but firebase depends on it internally
global.atob = atob // this tricks firebase into using the expected atob() method

const options = { encoding: FileSystem.EncodingType.Base64 }
const base64Response = await FileSystem.readAsStringAsync(uri, options) // uri from expo's camera
const stripped = base64Response.replace(/\s/g, '') // firebase storage doesn't like whitespaces in base64 images

firebase.storage().ref().child(path).putString(stripped, 'base64', { contentType: 'image/jpeg' })

further readings:

There was a fix for a blob issue that was just released:
https://github.com/expo/expo/issues/10464#issuecomment-703178030

Hey I am facing this issue right now, I just upgraded my expo install yesterday, what I should do next?

Edit1: Trying this

If you previously upgraded your project to SDK 39

  • For managed apps, change your react-native version to https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz. For bare apps, we recommend using react-native 0.63.3.
  • Run yarn upgrade to update to the latest version of packages in your project for their current semantic version range. npm does not have an equivalent command, so you can delete your package-lock.json and node_modules and run npm install again.
  • Build your standalone apps again.

Edit2: And we are back working:

const uploadImage = async (path, image) => {
  try {
    const imgRef = firebase.storage().ref(path);
    const imgTask = (await imgRef.put(await (await fetch(image)).blob())).task;
    return await imgTask.snapshot.ref.getDownloadURL();
  } catch (err) {
    return console.error('image upload error', err);
  }
};

Did the upgrade and all the steps on the link but having the same issue.