javascript - Firebase 存储 : string does not match format base64: invalid character found. 仅当调试关闭时

标签 javascript firebase react-native firebase-storage react-native-image-picker

我正在尝试将图像文件上传到 firebase 存储,保存下载 URL,并在上传完成后加载它。当我远程运行带有 debug js 的应用程序时,它工作正常。当我关闭 Debug模式时,它会停止处理无效格式异常。当我在真实设备(iOS 和 Android)中运行时也会发生同样的情况

来自 React Native Image Picker 的 base64 响应数据似乎是正确的

这是我的代码

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

  }

知道如何解决这个问题吗?

提前致谢。

最佳答案

经过一些研究和调试,我找到了问题的原因和解决方案。

为什么会这样?

Firebase 使用atob 方法对putstring 方法发送的base64 字符串进行解码。 但是,由于JavaScriptCore默认不支持atobbtoa,无法转换base64字符串,所以会触发this异常。

当我们在 debug javascript remote 模式下运行应用程序时,所有 javascript 代码都在 chrome 环境下运行,其中支持 atobbtoa。这就是代码在调试开启时有效而在关闭时无效的原因。

如何解决?

要在 React Native 中处理 atobbtoa,我们应该编写自己的编码/解码方法,或者安装一个库来为我们处理它。

在我的例子中,我更喜欢安装 base-64 lib

但这里有一个编码/解码脚本的例子:

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;

用法:

import Base64 from '[path to your script]';

const stringToEncode = 'xxxx';
Base64.btoa(scriptToEncode);

const stringToDecode = 'xxxx';
Base64.atob(stringToDecode);

选择使用自定义脚本或库后,现在我们必须将以下代码添加到 index.js 文件中:

import { decode, encode } from 'base-64';

if (!global.btoa) {
    global.btoa = encode;
}

if (!global.atob) {
    global.atob = decode;
}

AppRegistry.registerComponent(appName, () => App);

这将全局声明 atobbtoa。因此,无论何时在应用程序中调用这些函数,React Native 都会使用全局范围来处理它,然后从 base-64 触发 encodedecode 方法 库。

所以这就是 Base64 问题的解决方案。

然而,在解决这个问题之后,我又发现了另一个问题Firebase Storage: Max retry time for operation exceed。尝试上传较大的图像时请重试。正如 support to React Native uploads 所暗示的那样,firebase 似乎对 this issue 有一些限制。

我相信 react-native-firebase 可能不会在这方面遇到困难,因为它已经准备好在本地运行,而不是像 firebase 那样使用网络环境。我还没有测试它来确认,但看起来这将是处理它的最佳方法。

希望这对其他人有帮助。

关于javascript - Firebase 存储 : string does not match format base64: invalid character found. 仅当调试关闭时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52157253/

相关文章:

javascript - 未知提供商 : $resouceProvider <- $resouce

react-native - 在 React Native 中处理刷新 token

javascript - React Native、React Navigation集成问题 undefined is not an object getStateForAction

javascript - 链接仍然是 :hover'ed when opened from JS

javascript - Electron - Firebase 不支持的浏览器

javascript - 为什么无限对象不会破坏 javascript 中的流程?

firebase - 每当我进行新的 Firebase 托管部署时,如何发送通知?

java - 为什么我不能在查询 Firestore 中使用 whereEqualTo 条件

android - 如何在保留父 firebase 的同时删除子节点

javascript - 按值和字母表对数组进行排序 React Native