解决TextEncoder和TextDecoder在IE下不兼容的问题

# 背景国产加密sm4兼容IE处理

直接修改源码,不做引用处理;

对于只是utf-8编码,我们可以使用浏览器兼容的unescapeencodeURIComponent配合,进行模拟:

var encoder = new TextEncoder();
      encoder.encode("中文abc");
 //result : Uint8Array(9) [228, 184, 173, 230, 150, 135, 97, 98, 99]
unescape(encodeURIComponent("中文abc")).split("").map(val => val.charCodeAt());
//result : (9) [228, 184, 173, 230, 150, 135, 97, 98, 99]
1
2
3
4
5

同样的,解码如下:

var decoder = new TextDecoder();
    decoder.decode(Uint8Array.from([228, 184, 173, 230, 150, 135, 97, 98, 99]));
//result : 中文abc
decodeURIComponent(escape(String.fromCharCode(...[228, 184, 173, 230, 150, 135, 97, 98, 99])));
//result : 中文abc
1
2
3
4
5

为了兼容IE ,以ES5封装如下:

/**
 * @description 这是一个补丁包。用来解决新引入的stomp中引用textEncoder和textDecoder在IE下不兼容的问题
 * 由于stomp源码中只使用了最基本的utf8编码,故可以用支持ie的 unescape 和 encodeURIComponent伪装该函数
 */
(function(window) {
    if(typeof TextEncoder=="function") {return;}
    function _TextEncoder() {
        //--DO NOTHING
    }
    _TextEncoder.prototype.encode = function(s) {
        //return unescape(encodeURIComponent(s)).split('').map(function(val) {return val.charCodeAt();});
    var data=unescape(encodeURIComponent(s)).split('').map(function(val) {return val.charCodeAt();});
    return typeof Uint8Array=="function"?new Uint8Array(data):data;//new TextEncoder().encode返回Uint8Array
    };
    function _TextDecoder() {
        //--DO NOTHING
    }   
    _TextDecoder.prototype.decode = function(code_arr) {
        return decodeURIComponent(escape(String.fromCharCode.apply(null, code_arr)));
    };
    window.TextEncoder = _TextEncoder;
    window.TextDecoder = _TextDecoder;
})(this);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

加密封装:

const base64js = require('base64-js');

var utf8Encodings = ['utf8', 'utf-8', 'unicode-1-1-utf-8'];

function MyTextEncoder(encoding) {
  if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) {
    throw new RangeError('Invalid encoding type. Only utf-8 is supported');
  } else {
    this.encoding = 'utf-8';
    this.encode = function(str) {
      if (typeof str !== 'string') {
        throw new TypeError('passed argument must be of tye string');
      }
      var binstr = unescape(encodeURIComponent(str)),
        arr = new Uint8Array(binstr.length);
      const split = binstr.split('');
      for (let i = 0; i < split.length; i++) {
        arr[i] = split[i].charCodeAt(0);
      }
      return arr;
    };
  }
}

function MyTextDecoder(encoding) {
  if (utf8Encodings.indexOf(encoding) < 0 && typeof encoding !== 'undefined' && encoding != null) {
    throw new RangeError('Invalid encoding type. Only utf-8 is supported');
  } else {
    this.encoding = 'utf-8';
    this.decode = function(view, options) {
      if (typeof view === 'undefined') {
        return '';
      }

      var stream = typeof options !== 'undefined' && stream in options ? options.stream : false;
      if (typeof stream !== 'boolean') {
        throw new TypeError('stream option must be boolean');
      }

      if (!ArrayBuffer.isView(view)) {
        throw new TypeError('passed argument must be an array buffer view');
      } else {
        var arr = new Uint8Array(view.buffer, view.byteOffset, view.byteLength),
          charArr = new Array(arr.length);
        for (let i = 0; i < arr.length; i++) {
          charArr[i] = String.fromCharCode(arr[i]);
        }
        return decodeURIComponent(escape(charArr.join('')));
      }
    };
  }
}

class Crypt {
  /**
   * Converts a JS string to an UTF-8 uint8array.
   *
   * @static
   * @param {String} str 16-bit unicode string.
   * @return {Uint8Array} UTF-8 Uint8Array.
   * @memberof Crypt
   */
  static stringToArrayBufferInUtf8(str) {
    // if not browser env, then require node.js's util. otherwise just use window's
    // const TextEncoder =
    //   typeof window === 'undefined' ? require('util').TextEncoder : window.TextEncoder;
    // always utf-8
    const TextEncoder = window.TextEncoder ? window.TextEncoder : MyTextEncoder;
    let encoder = new TextEncoder();
    console.log(encoder);
    return encoder.encode(str);
  }

  /**
   * Converts an UTF-8 uint8array to a JS string.
   *
   * @static
   * @param {Uint8Array} strBuffer UTF-8 Uint8Array.
   * @return {String} 16-bit unicode string.
   * @memberof Crypt
   */
  static utf8ArrayBufferToString(strBuffer) {
    // if not browser env, then require node.js's util. otherwise just use window's
    // const TextDecoder =
    //   typeof window === 'undefined' ? require('util').TextDecoder : window.TextDecoder;
    const TextDecoder = window.TextDecoder ? window.TextDecoder : MyTextDecoder;
    let decoder = new TextDecoder('utf-8');
    return decoder.decode(strBuffer);
  }

  /**
   * crypt a utf8 byteArray to base64 string
   *
   * @static
   * @param {Uint8Array} strBuffer UTF-8 Uint8Array.
   * @returns {String} base64 str
   * @memberof Crypt
   */
  static arrayBufferToBase64(strBuffer) {
    return base64js.fromByteArray(strBuffer);
  }

  /**
   * crypt base64 stringa to utf8 byteArray
   *
   * @static
   * @param {String} base64 str
   * @returns {Uint8Array} strBuffer UTF-8 Uint8Array.
   * @memberof Crypt
   */
  static base64ToArrayBuffer(base64) {
    return base64js.toByteArray(base64);
  }
}
export default Crypt;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
上次更新: 2022/04/15, 05:41:29
×