js implementation applet Wx Arraybuffertobase64 method


In applet development, it is necessary to obtain the interface request arrayBuffer data, converted to base64 format data for picture display.

Wechat applet provides Wx Arraybuffertobase64 method, but unfortunately, this method has been abandoned since the basic library version 2.4.0 and is no longer recommended.

Although at present, even if the basic library version of the applet is 2.22.0, it can be used normally. However, it is uncertain when the method will be deleted in the updated basic library in the future. This will bring hidden dangers on the project.

Therefore, you need to implement the process of converting arrayBuffer to base64.

Explore the process of failure

  • new FileReader() cannot be used in applets. Cannot convert data to base64 using readAsDataURL method in new FileReader() instance. If you are not familiar with this method, you can view it Introduction to FileReader
  • URL.createObjectURL cannot be used in applets. This method cannot be used to convert the data into an address in memory, which can be referenced by the image tag. If you are not familiar with this method, you can view it URL.createObjectURL explanation
  • window.btoa cannot be used in applets. Cannot convert text directly to base64 format.

All right, all roads are blocked. Then it's time to pave the way and build the bridge by yourself.

The arrayBuffer of the jammed case is converted to base64

The starting condition of the problem is arrayBuffer data, and the expected result is to finally form base64 format data. Then start solving.

First, let's talk about arrayBuffer.

In JavaScript, there is a very common reference data type Array, in which you can put strings, numbers, objects, Boolean values and so on. It can be stored in the pile and can be increased or decreased freely.

The ArrayBuffer object is used to represent a generic, fixed length raw binary data buffer. It is a byte array, which is usually called "byte array" in other languages. It was born to solve a problem: operating binary data.

Binary data only composed of 0 and 1 is often very huge, and thousands of bytes can be said to be common. At this time, the traditional Array is very inefficient when dealing with binary data, so ArrayBuffer appears. It is stored in the stack as a special memory area and fetches data very quickly.

Let's initialize a buffer instance through new ArrayBuffer(10) to see what we will get.

let buffer = new ArrayBuffer(10);
console.log(buffer);

// Displayed on the console as follows
ArrayBuffer(10)
byteLength: 10
[[Prototype]]: ArrayBuffer
[[Int8Array]]: Int8Array(10)
[[Uint8Array]]: Uint8Array(10)
[[Int16Array]]: Int16Array(5)
[[ArrayBufferByteLength]]: 10
[[ArrayBufferData]]: 1367

It can be seen that several "views" are mainly stored in the ArrayBuffer. Int8Array represents an 8-bit signed integer array, Int16Array represents a 16 bit signed integer array, and Uint8Array represents an 8-bit unsigned integer array.

Of course, if we want to fetch the array Int8Array, for example, we can't directly use the buffer Int8Array. This is because ArrayBuffer cannot be read or written directly through subscripts. We need to convert it into a typed array.

You cannot directly manipulate the contents of ArrayBuffer, but through type array objects or DataView objects. They will represent the data in the buffer in specific formats and read and write the contents of the buffer through these formats.

const myTypedArray = new Uint8Array(buffer)

After the conversion, we can not only index the typed Array by subscript, but also obtain its length. Of course, TypedArray is still slightly different from ordinary Array. That is, assuming that we use the index syntax beyond the boundary to obtain the Array elements, TypedArray will not search in the prototype chain.

Now that we have this typed array, it's time to turn it into a normal string. Look at string The fromcharcode function accepts a sequence of code units and outputs an ordinary string. The typed array we just got contains code units.

const str = String.fromCharCode(...myTypedArray)

Here we use the extension operator Solve the code unit of the type array and turn it all at once to get an ordinary string.

Finally, we need to use a window object method, that is, btoa method. Its function is to encode an ordinary string into a string in base-64 format.

The above seems very good, but in the last step, btoa, there is no such method to use in the applet. You need to implement btoa this method yourself.

Key points: implementation of btoa

Because this function has been implemented in the browser, it is more used in applet and node environment. Therefore, the overall design is based on module Exports is the output of the method and is introduced in the form of require.

Output method

module.exports = {
  btoa: ...,
  atob: ...
}

Import file

const { btoa } =  require('./base64')

base64.js

var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
  a256 = '',
  r64 = [256],
  r256 = [256],
  i = 0;

var UTF8 = {

  /**
   * Encode multi-byte Unicode string into utf-8 multiple single-byte characters
   * (BMP / basic multilingual plane only)
   *
   * Chars in range U+0080 - U+07FF are encoded in 2 chars, U+0800 - U+FFFF in 3 chars
   *
   * @param {String} strUni Unicode string to be encoded as UTF-8
   * @returns {String} encoded string
   */
  encode: function (strUni) {
    // use regular expressions & String.replace callback function for better efficiency
    // than procedural approaches
    var strUtf = strUni.replace(/[\u0080-\u07ff]/g, // U+0080 - U+07FF => 2 bytes 110yyyyy, 10zzzzzz
      function (c) {
        var cc = c.charCodeAt(0);
        return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
      })
      .replace(/[\u0800-\uffff]/g, // U+0800 - U+FFFF => 3 bytes 1110xxxx, 10yyyyyy, 10zzzzzz
        function (c) {
          var cc = c.charCodeAt(0);
          return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3F, 0x80 | cc & 0x3f);
        });
    return strUtf;
  },

  /**
   * Decode utf-8 encoded string back into multi-byte Unicode characters
   *
   * @param {String} strUtf UTF-8 string to be decoded back to Unicode
   * @returns {String} decoded string
   */
  decode: function (strUtf) {
    // note: decode 3-byte chars first as decoded 2-byte strings could appear to be 3-byte char!
    var strUni = strUtf.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars
      function (c) { // (note parentheses for precence)
        var cc = ((c.charCodeAt(0) & 0x0f) << 12) | ((c.charCodeAt(1) & 0x3f) << 6) | (c.charCodeAt(2) & 0x3f);
        return String.fromCharCode(cc);
      })
      .replace(/[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars
        function (c) { // (note parentheses for precence)
          var cc = (c.charCodeAt(0) & 0x1f) << 6 | c.charCodeAt(1) & 0x3f;
          return String.fromCharCode(cc);
        });
    return strUni;
  }
};

while (i < 256) {
  var c = String.fromCharCode(i);
  a256 += c;
  r256[i] = i;
  r64[i] = b64.indexOf(c);
  ++i;
}

function code(s, discard, alpha, beta, w1, w2) {
  s = String(s);
  var buffer = 0,
    i = 0,
    length = s.length,
    result = '',
    bitsInBuffer = 0;

  while (i < length) {
    var c = s.charCodeAt(i);
    c = c < 256 ? alpha[c] : -1;

    buffer = (buffer << w1) + c;
    bitsInBuffer += w1;

    while (bitsInBuffer >= w2) {
      bitsInBuffer -= w2;
      var tmp = buffer >> bitsInBuffer;
      result += beta.charAt(tmp);
      buffer ^= tmp << bitsInBuffer;
    }
    ++i;
  }
  if (!discard && bitsInBuffer > 0) result += beta.charAt(buffer << (w2 - bitsInBuffer));
  return result;
}

var Plugin = function (dir, input, encode) {
  return input ? Plugin[dir](input, encode) : dir ? null : this;
};

Plugin.btoa = Plugin.encode = function (plain, utf8encode) {
  plain = Plugin.raw === false || Plugin.utf8encode || utf8encode ? UTF8.encode(plain) : plain;
  plain = code(plain, false, r256, b64, 8, 6);
  return plain + '===='.slice((plain.length % 4) || 4);
};

Plugin.atob = Plugin.decode = function (coded, utf8decode) {
  coded = coded.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  coded = String(coded).split('=');
  var i = coded.length;
  do {
    --i;
    coded[i] = code(coded[i], true, r64, a256, 6, 8);
  } while (i > 0);
  coded = coded.join('');
  return Plugin.raw === false || Plugin.utf8decode || utf8decode ? UTF8.decode(coded) : coded;
};
module.exports = {
  btoa: Plugin.btoa,
  atob: Plugin.atob
}

Make a good fruit

Sometimes the background transmits image resources to the front end through arrayBuffer. At this time, in order to display normally, we also need to splice data: image / jpeg in front of the converted Base64 string; base64,

So let's sort it out and get such a function:

const { btoa } =  require('./base64')
const arrayBufferToBase64Img = (buffer) => {
  const str = String.fromCharCode(...new Uint8Array(buffer));
  return `data:image/jpeg;base64,${btoa(str)}`;
}

The whole process is as follows:

Get an ArrayBuffer - > Convert to typed array for normal reading (uint8array) - > Convert to normal string (string. Fromcharcode) - > Convert to base64 string (btoa)

Keywords: Javascript Front-end Mini Program

Added by spectacularstuff on Thu, 03 Mar 2022 18:06:47 +0200