Firebase Storage: строка не соответствует формату base64: найден недопустимый символ. Только когда отладка выключена - PullRequest
0 голосов
/ 04 сентября 2018

Я пытаюсь загрузить файл изображения в хранилище Firebase, сохранить URL-адрес загрузки и загрузить его после завершения загрузки. Когда я запускаю приложение с Debug JS удаленно на нем работает нормально. Когда я отключаю режим отладки, он перестает работать с недопустимым исключением формата. То же самое происходит, когда я работаю на реальном устройстве (iOS и Android)

Данные ответа base64 от React Native Image Picker, похоже, верны

Вот мой код

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

  }

Есть идеи, как это решить?

Заранее спасибо.

1 Ответ

0 голосов
/ 05 сентября 2018

После некоторых исследований и отладки я нашел причину проблемы и решение для нее.

Почему это происходит?

Firebase использует метод atob для декодирования строки base64, отправленной методом putstring. Однако, поскольку JavaScriptCore не имеет поддержки по умолчанию atob и btoa, строка base64 не может быть преобразована, поэтому вызывается это исключение.

Когда мы запускаем приложение в режиме отладки javascript удаленно, весь код javascript запускается в среде chrome, где поддерживаются atob и btoa. Вот почему код работает, когда включена отладка, и не работает, когда она выключена.

Как решить?

Чтобы обработать atob и btoa в React Native, мы должны либо написать наш собственный метод кодирования / декодирования, либо установить lib для его обработки за нас.

В моем случае я предпочел установить 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);

После выбора использования пользовательского сценария или библиотеки 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);

Это объявит atob и btoa во всем мире. Поэтому всякий раз, когда в приложении вызываются эти функции, React Native будет использовать глобальную область действия для его обработки, а затем запускать методы encode и decode из base-64 lib.

Так что это решение проблемы Base64.

Однако после того, как это было решено, я обнаружил еще одну проблему Firebase Storage: Max retry time for operation exceed. Please try again при попытке загрузить изображения большего размера. Кажется, что firebase имеет некоторые ограничения на поддержку React Native закачек , как эта проблема предполагает.

Я полагаю, что react-native-firebase может и не бороться с этим, поскольку он уже подготовлен для собственного запуска, вместо использования веб-среды, как firebase. Я еще не проверял это, чтобы подтвердить, но, похоже, это будет лучшим подходом к решению проблемы.

Надеюсь, это может быть полезно для кого-то еще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...