[英]Firebase Storage: string does not match format base64: invalid character found. Only when debug is off
I'm trying to upload an image file to firebase storage, save the download URL, and load it after the upload is completed.我正在尝试上传一个图片文件到firebase存储,保存下载URL,上传完成后再加载。 When I run the app with debug js remotely on it works fine.当我远程运行带有 debug js 的应用程序时,它工作正常。 When I turn off debug mode it stops working with the invalid format exception.当我关闭调试模式时,它会停止处理无效格式异常。 The same happens when I run in a real device (both iOS and Android)当我在真实设备(iOS 和 Android)中运行时也会发生同样的情况
The base64 response data from React Native Image Picker seems to be correct来自 React Native Image Picker 的 base64 响应数据似乎是正确的
Here's my code这是我的代码
...
import * as ImagePicker from 'react-native-image-picker'; //0.26.10
import firebase from 'firebase'; //4.9.1
...
handleImagePicker = () => {
const { me } = this.props;
const options = {
title: 'Select pic',
storageOptions: {
skipBackup: true,
path: 'images'
},
mediaType: 'photo',
quality: 0.5,
};
ImagePicker.showImagePicker(options, async (response) => {
const storageRef = firebase.storage().ref(`/profile-images/user_${me.id}.jpg`);
const metadata = {
contentType: 'image/jpeg',
};
const task = storageRef.putString(response.data, 'base64', metadata);
return new Promise((resolve, reject) => {
task.on(
'state_changed',
(snapshot) => {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
},
(error) =>
console.log(error),
() => {
this.onChangeProfileImage();
}
);
});
}
}
onChangeProfileImage = async () => {
const { me } = this.props;
const storageRef = firebase.storage().ref(`/profile-images/user_${me.id}.jpg`);
const profileImageUrl = await new Promise((resolve, reject) => {
storageRef.getDownloadURL()
.then((url) => {
resolve(url);
})
.catch((error) => {
console.log(error);
});
});
// some more logic to store profileImageUrl in the database
}
Any idea how to solve this?知道如何解决这个问题吗?
Thanks in advance.提前致谢。
The problem is now solved using fetch() API. The promise returned can be converted to blob which you can upload to firebase/storage现在使用fetch() API 解决了问题。返回的 promise 可以转换为 blob,您可以将其上传到 firebase/storage
Here is an example这是一个例子
let storageRef = storage().ref();
let imageName = data.name + "image";
let imagesRef = storageRef.child(`images/${imageName}`);
const response = await fetch(image);
const blob = await response.blob(); // Here is the trick
imagesRef
.put(blob)
.then((snapshot) => {
console.log("uploaded an image.");
})
.catch((err) => console.log(err));
After some research and debug I found the cause of the issue and a solution for it. 经过一些研究和调试后,我找到了问题的原因并找到了解决方案。
Firebase uses atob
method to decode the base64 string sent by putstring
method. Firebase使用atob
方法解码putstring
方法发送的base64字符串。 However, since JavaScriptCore doesn't have a default support to atob
and btoa
, the base64 string can't be converted, so this exception is triggered. 但是,由于JavaScriptCore没有对atob
和btoa
的默认支持,因此无法转换base64字符串,因此会触发此异常。
When we run the app in debug javascript remotely mode, all javascript code is run under chrome environment, where atob
and btoa
are supported. 当我们在调试javascript远程模式下运行应用程序时,所有javascript代码都在chrome环境下运行,其中支持atob
和btoa
。 That's why the code works when debug is on and doesn't when its off. 这就是为什么代码在调试打开时工作,而在关闭时不工作的原因。
To handle atob
and btoa
in React Native, we should either write our own encode/decode method, or install a lib to handle it for us. 要在React Native中处理atob
和btoa
,我们应该编写自己的编码/解码方法,或者安装一个lib来为我们处理它。
In my case I preferred to install base-64
lib 在我的情况下,我更喜欢安装base-64
lib
But here's an example of a encode/decode script: 但这是一个编码/解码脚本的例子:
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
const Base64 = {
btoa: (input:string = '') => {
let str = input;
let output = '';
for (let block = 0, charCode, i = 0, map = chars;
str.charAt(i | 0) || (map = '=', i % 1);
output += map.charAt(63 & block >> 8 - i % 1 * 8)) {
charCode = str.charCodeAt(i += 3/4);
if (charCode > 0xFF) {
throw new Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
}
block = block << 8 | charCode;
}
return output;
},
atob: (input:string = '') => {
let str = input.replace(/=+$/, '');
let output = '';
if (str.length % 4 == 1) {
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (let bc = 0, bs = 0, buffer, i = 0;
buffer = str.charAt(i++);
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
buffer = chars.indexOf(buffer);
}
return output;
}
};
export default Base64;
Usage: 用法:
import Base64 from '[path to your script]';
const stringToEncode = 'xxxx';
Base64.btoa(scriptToEncode);
const stringToDecode = 'xxxx';
Base64.atob(stringToDecode);
After choosing either to use the custom script or the lib, now we must add the following code to the index.js
file: 在选择使用自定义脚本或lib之后,现在我们必须将以下代码添加到index.js
文件中:
import { decode, encode } from 'base-64';
if (!global.btoa) {
global.btoa = encode;
}
if (!global.atob) {
global.atob = decode;
}
AppRegistry.registerComponent(appName, () => App);
This will declare atob
and btoa
globally. 这将在全球宣布atob
和btoa
。 So whenever in the app those functions are called, React Native will use the global scope to handle it, and then trigger the encode
and decode
methods from base-64
lib. 因此,只要在应用程序中调用这些函数,React Native将使用全局范围来处理它,然后从base-64
lib触发encode
和decode
方法。
So this is the solution for Base64 issue. 所以这是Base64问题的解决方案。
However, after this is solved, I found another issue Firebase Storage: Max retry time for operation exceed. Please try again
但是,在此问题解决后,我发现Firebase Storage: Max retry time for operation exceed. Please try again
另一个问题Firebase Storage: Max retry time for operation exceed. Please try again
Firebase Storage: Max retry time for operation exceed. Please try again
when trying to upload larger images. Firebase Storage: Max retry time for operation exceed. Please try again
上传较大的图片时Firebase Storage: Max retry time for operation exceed. Please try again
。 It seems that firebase
has some limitation on support to React Native uploads , as this issue suggests. 正如这个问题所暗示的那样, firebase
似乎对React Native上传的支持有一些限制。
I believe that react-native-firebase
may not struggle on this since it's already prepared to run natively, instead of using the web environment as firebase
does. 我相信react-native-firebase
可能不会在这方面挣扎,因为它已经准备好本地运行,而不是像firebase
一样使用web环境。 I didn't test it yet to confirm, but it looks like this will be the best approach to handle it. 我还没有测试它确认,但看起来这将是处理它的最佳方法。
Hope this can be helpful for someone else. 希望这对其他人有帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.