【问题标题】:Firebase Storage: string does not match format base64: invalid character found. Only when debug is offFirebase 存储:字符串与格式 base64 不匹配:发现无效字符。仅当调试关闭时
【发布时间】:2019-02-08 22:53:18
【问题描述】:

我正在尝试将图像文件上传到 Firebase 存储,保存下载 URL,并在上传完成后加载它。当我远程运行带有调试 js 的应用程序时,它工作正常。当我关闭调试模式时,它会停止处理无效格式异常。当我在真实设备(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

  }

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

提前致谢。

【问题讨论】:

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


    【解决方案1】:

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

    为什么会这样?

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

    当我们在远程调试javascript模式下运行应用程序时,所有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. Please try again。正如this issue 所暗示的那样,firebase 似乎对support to React Native uploads 有一些限制。

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

    希望这对其他人有帮助。

    【讨论】:

    • 顺便说一句,@soutot,您是否使用 fetch() 从缓存中读取文件?它对我不起作用,所以我尝试改用 expo 的 FileSystem.readAsStringAsync。
    • 我开始做其他项目并没有完成这个,所以我没有实现缓存读取。我当时的想法是下载图像并存储在本地存储中。如果存储了任何有效图像,则从中读取,否则再次获取。我认为它可能会起作用。
    • 好吧,祝你好运:) fetch 似乎为我工作了一段时间,然后停止工作。 github.com/expo/firebase-storage-upload-example/issues/14
    • 但是您正在将字符串编码为 base64 字符串,图像的解决方案是什么?
    【解决方案2】:

    现在使用 fetch() API 解决了这个问题。返回的 Promise 可以转换为可以上传到 firebase/storage 的 blob

    这是一个例子

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

    【讨论】:

      猜你喜欢
      • 2017-05-09
      • 1970-01-01
      • 2019-04-05
      • 2018-03-04
      • 2014-07-05
      • 2013-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多