Reputation: 1926
I am currently working on an App. The workflow I currently have is fairly simple.
A user creates an account, and then is taken to a page to populate their profile information. Name, description, and a few images.
I use expo's ImagePicker
to get the image:
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 0.1,
allowsEditing: true,
aspect: [2, 3],
base64: true
});
Originally, I was using this to upload the images:
// 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 ref = firebase
.storage()
.ref()
.child(uuid.v4());
const snapshot = await ref.put(blob);
// We're done with the blob, close and release it
blob.close();
let url = await snapshot.ref.getDownloadURL();
return url;
The problem here is I looped through that function about 6 times, and I kept getting some obscure error.
Currently, I am attempting to upload the images using this:
const ref = firebase
.storage()
.ref()
.child(uuid.v4());
const snapshot = await ref.putString(b64Url, "data_url");
This works well on web, but in the native app I get the error:
FirebaseStorageError {
"code_": "storage/invalid-format",
"message_": "Firebase Storage: String does not match format 'base64': Invalid character found",
"name_": "FirebaseError",
"serverResponse_": null,
}
The last comment on this issue outlines the problem. To break it down: atob
doesn't exist. This is the sole problem behind the error. To fix, I polyfilled it like this:
import { decode, encode } from "base-64";
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob) {
global.atob = decode;
}
However, the second problem is that:
Firebase also tries to use the native Blob class (implemented by react-native), but the react-native version of Blob incorrectly converts the Uint8Array data to a string, corrupting the upload.
I tried his solution of delete
ing global.Blob
and restoring it after the upload. Firebase must have become dependent upon blob though, because now it errors out since Edit: Blob is actually being called somewhere in AppEntry.bundle, the uploading works correctly.Blob
doesn't exist.
I would like to keep my app in a managed workflow, so I would very much prefer not to eject.
My questions are as follows:
react-native
is the broken Blob code that:incorrectly converts the Uint8Array data to a string
Upvotes: 1
Views: 2117
Reputation: 1926
The solution I ended up following was this:
async function uploadImageAsync(uri) {
const ref = firebase
.storage()
.ref()
.child(uuid.v4());
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", uri, true);
xhr.send(null);
});
var mimeString = uri
.split(",")[0]
.split(":")[1]
.split(";")[0];
const snapshot = await ref.put(blob, { contentType: mimeString });
let url = await snapshot.ref.getDownloadURL();
return url;
}
I found that I could not seem to get firebase's putString
function to work, but I could create a blob out of the string using XMLHttpRequest
. Then I just upload the blob to firebase
Upvotes: 2