Firebase Storage: string does not match format base64: invalid character found. Only when debug is off

前端 未结 1 972
执念已碎
执念已碎 2020-12-10 20:24

I\'m trying to upload an image file to firebase storage, save the download URL, and load it after the upload is completed. When I run the app with debug js remotely on it wo

相关标签:
1条回答
  • 2020-12-10 21:05

    After some research and debug I found the cause of the issue and a solution for it.

    Why does it happen?

    Firebase uses atob method to decode the base64 string sent by putstring method. 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.

    When we run the app in debug javascript remotely mode, all javascript code is run under chrome environment, where atob and btoa are supported. That's why the code works when debug is on and doesn't when its off.

    How to solve?

    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.

    In my case I preferred to install 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:

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

    So this is the solution for Base64 issue.

    However, after this is solved, I found another issue Firebase Storage: Max retry time for operation exceed. Please try again when trying to upload larger images. It seems that firebase has some limitation on support to React Native uploads, as this issue suggests.

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

    0 讨论(0)
提交回复
热议问题