Java 和 JavaScript 真正通用的Base64编码详解

时间:2022-09-02 22:21:57

 JavaJavaScript Base64编码

在开发Java  Web应用的时候,可能会在服务器端用Java做Base64编码,而在客户端用JavaScript进行解码。这样就要求两边的Base64编码机制保持一致。

使用Base64编码,可能会碰到各种奇怪情况,甚至怀疑编码有bug。但实际上不是这样的。Base64理论上操作的对象不是字符串而是字节数组。它的原理就是把ASCII码的255个字符缩小到用64个来表示。具体就是原来三个字节用四个字节表示,编码后长度有一定的增长。

1) 最好一次编码,避免分段编码,确实要分段编码,每一段字节数应该是3的倍数。

长字节流,如果要边读取边编码,每一段必须是3的倍数,否则就可能在还原的时候出乱。一般人喜欢用2的乘方来定义数组,例如 byte[1024],因为不是3的倍数,可能还原时出错。正确的例子是:

byte[] bs=new byte[3*100] ....inputStream.read(bs)......encode(bs )....

对于字符串,一般要整个一次编码,以避免分段编码出错。

当然,如果你分段编码,还原的时候也是一段一段地还原,那是没有问题的。

2)确保字符串还原的时候按照原来的编码还原。

因为它操作的是字节数组,所以对于GBK编码的汉字和UTF-8编码汉字,经过 Base64编码后结果是不一样的。例如“我们”这两个字如果是GBK编码,转成Base64后就是ztLDxw== ;如果是UTF-8编码,转成Base64后就是5oiR5Lus。

也就是 “我们” ==》  getBytes("GBK") ==> Base64

所以Java这边用什么编码转换,在JavaScript那边就要用什么编码还原。要保证Java和JavaScript通用,我们采用Unicode的编码(JavaScript转成UTF-8、GBK不方便,所以就采用了其本身的Unicode编码),具体如下:

服务器端:

1)用getBytes("Unicode")转成Unicode字节数组。

2) 编码成Base64字符串

3)传送到客户端

客户端:

 

1)Base64 解码成字节数组

2)按Unicode还原

代码如下(相关的函数看附件):

Base64.encode(data,"Unicode"); //java 端编码

decode64(data);   //javascript解码

附一:Java中Base64编码

?
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package websharp.util;
public class Base64 {
  private static final byte[] encodingTable = {
      (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
      (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J',
      (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O',
      (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T',
      (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y',
      (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd',
      (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i',
      (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n',
      (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's',
      (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
      (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2',
      (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7',
      (byte) '8', (byte) '9', (byte) '+', (byte) '/'
    };
  private static final byte[] decodingTable;
  static {
    decodingTable = new byte[128];
    for (int i = 0; i < 128; i++) {
      decodingTable[i] = (byte) -1;
    }
    for (int i = 'A'; i <= 'Z'; i++) {
      decodingTable[i] = (byte) (i - 'A');
    }
    for (int i = 'a'; i <= 'z'; i++) {
      decodingTable[i] = (byte) (i - 'a' + 26);
    }
    for (int i = '0'; i <= '9'; i++) {
      decodingTable[i] = (byte) (i - '0' + 52);
    }
    decodingTable['+'] = 62;
    decodingTable['/'] = 63;
  }
  public static byte[] encode(byte[] data,int offset) {
    byte[] bytes;
    int realCount=data.length-offset;
    int modulus = realCount % 3;
    if (modulus == 0) {
      bytes = new byte[(4 * realCount) / 3];
    } else {
      bytes = new byte[4 * ((realCount / 3) + 1)];
    }
    int dataLength = (data.length - modulus);
    int a1;
    int a2;
    int a3;
    for (int i = offset, j = 0; i < dataLength; i += 3, j += 4) {
      a1 = data[i] & 0xff;
      a2 = data[i + 1] & 0xff;
      a3 = data[i + 2] & 0xff;
      bytes[j] = encodingTable[(a1 >>> 2) & 0x3f];
      bytes[j + 1] = encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f];
      bytes[j + 2] = encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f];
      bytes[j + 3] = encodingTable[a3 & 0x3f];
    }
    int b1;
    int b2;
    int b3;
    int d1;
    int d2;
    switch (modulus) {
    case 0: /* nothing left to do */
      break;
    case 1:
      d1 = data[data.length - 1] & 0xff;
      b1 = (d1 >>> 2) & 0x3f;
      b2 = (d1 << 4) & 0x3f;
      bytes[bytes.length - 4] = encodingTable[b1];
      bytes[bytes.length - 3] = encodingTable[b2];
      bytes[bytes.length - 2] = (byte) '=';
      bytes[bytes.length - 1] = (byte) '=';
      break;
    case 2:
      d1 = data[data.length - 2] & 0xff;
      d2 = data[data.length - 1] & 0xff;
      b1 = (d1 >>> 2) & 0x3f;
      b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
      b3 = (d2 << 2) & 0x3f;
      bytes[bytes.length - 4] = encodingTable[b1];
      bytes[bytes.length - 3] = encodingTable[b2];
      bytes[bytes.length - 2] = encodingTable[b3];
      bytes[bytes.length - 1] = (byte) '=';
      break;
    }
    return bytes;
  }
  public static byte[] decode(byte[] data) {
    byte[] bytes;
    byte b1;
    byte b2;
    byte b3;
    byte b4;
    data = discardNonBase64Bytes(data);
    if (data[data.length - 2] == '=') {
      bytes = new byte[(((data.length / 4) - 1) * 3) + 1];
    } else if (data[data.length - 1] == '=') {
      bytes = new byte[(((data.length / 4) - 1) * 3) + 2];
    } else {
      bytes = new byte[((data.length / 4) * 3)];
    }
    for (int i = 0, j = 0; i < (data.length - 4); i += 4, j += 3) {
      b1 = decodingTable[data[i]];
      b2 = decodingTable[data[i + 1]];
      b3 = decodingTable[data[i + 2]];
      b4 = decodingTable[data[i + 3]];
      bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
      bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
      bytes[j + 2] = (byte) ((b3 << 6) | b4);
    }
    if (data[data.length - 2] == '=') {
      b1 = decodingTable[data[data.length - 4]];
      b2 = decodingTable[data[data.length - 3]];
      bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
    } else if (data[data.length - 1] == '=') {
      b1 = decodingTable[data[data.length - 4]];
      b2 = decodingTable[data[data.length - 3]];
      b3 = decodingTable[data[data.length - 2]];
      bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
      bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
    } else {
      b1 = decodingTable[data[data.length - 4]];
      b2 = decodingTable[data[data.length - 3]];
      b3 = decodingTable[data[data.length - 2]];
      b4 = decodingTable[data[data.length - 1]];
      bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
      bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
      bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
    }
    return bytes;
  }
  public static byte[] decode(String data) {
    byte[] bytes;
    byte b1;
    byte b2;
    byte b3;
    byte b4;
    data = discardNonBase64Chars(data);
    if (data.charAt(data.length() - 2) == '=') {
      bytes = new byte[(((data.length() / 4) - 1) * 3) + 1];
    } else if (data.charAt(data.length() - 1) == '=') {
      bytes = new byte[(((data.length() / 4) - 1) * 3) + 2];
    } else {
      bytes = new byte[((data.length() / 4) * 3)];
    }
    for (int i = 0, j = 0; i < (data.length() - 4); i += 4, j += 3) {
      b1 = decodingTable[data.charAt(i)];
      b2 = decodingTable[data.charAt(i + 1)];
      b3 = decodingTable[data.charAt(i + 2)];
      b4 = decodingTable[data.charAt(i + 3)];
      bytes[j] = (byte) ((b1 << 2) | (b2 >> 4));
      bytes[j + 1] = (byte) ((b2 << 4) | (b3 >> 2));
      bytes[j + 2] = (byte) ((b3 << 6) | b4);
    }
    if (data.charAt(data.length() - 2) == '=') {
      b1 = decodingTable[data.charAt(data.length() - 4)];
      b2 = decodingTable[data.charAt(data.length() - 3)];
      bytes[bytes.length - 1] = (byte) ((b1 << 2) | (b2 >> 4));
    } else if (data.charAt(data.length() - 1) == '=') {
      b1 = decodingTable[data.charAt(data.length() - 4)];
      b2 = decodingTable[data.charAt(data.length() - 3)];
      b3 = decodingTable[data.charAt(data.length() - 2)];
      bytes[bytes.length - 2] = (byte) ((b1 << 2) | (b2 >> 4));
      bytes[bytes.length - 1] = (byte) ((b2 << 4) | (b3 >> 2));
    } else {
      b1 = decodingTable[data.charAt(data.length() - 4)];
      b2 = decodingTable[data.charAt(data.length() - 3)];
      b3 = decodingTable[data.charAt(data.length() - 2)];
      b4 = decodingTable[data.charAt(data.length() - 1)];
      bytes[bytes.length - 3] = (byte) ((b1 << 2) | (b2 >> 4));
      bytes[bytes.length - 2] = (byte) ((b2 << 4) | (b3 >> 2));
      bytes[bytes.length - 1] = (byte) ((b3 << 6) | b4);
    }
    for(int i=0;i<bytes.length;i++) System.out.println(","+bytes[i]);
    return bytes;
  }
  private static byte[] discardNonBase64Bytes(byte[] data) {
    byte[] temp = new byte[data.length];
    int bytesCopied = 0;
    for (int i = 0; i < data.length; i++) {
      if (isValidBase64Byte(data[i])) {
        temp[bytesCopied++] = data[i];
      }
    }
    byte[] newData = new byte[bytesCopied];
    System.arraycopy(temp, 0, newData, 0, bytesCopied);
    return newData;
  }
  private static String discardNonBase64Chars(String data) {
    StringBuffer sb = new StringBuffer();
    int length = data.length();
    for (int i = 0; i < length; i++) {
      if (isValidBase64Byte((byte) (data.charAt(i)))) {
        sb.append(data.charAt(i));
      }
    }
    return sb.toString();
  }
  private static boolean isValidBase64Byte(byte b) {
    if (b == '=') {
      return true;
    } else if ((b < 0) || (b >= 128)) {
      return false;
    } else if (decodingTable[b] == -1) {
      return false;
    }
    return true;
  }
  public static String encode(String data,String charset)throws Exception
  {
    // byte[] result = (data.getBytes("Unicode"));
     if(data==null || data.length()==0) return data;
     int offset=0;
     // getBytes("unicode")转完后会在前头加上两字节”FE“
     byte[] result=encode (data.getBytes(charset),offset);
     StringBuffer sb=new StringBuffer(result.length);
     for (int i=0;i<result.length;i++)  sb.append((char)result[i]);
     return sb.toString();
  }
  public static String decode(String data,String charset)throws Exception
  {
    if(data==null || data.length()==0) return data;
    return new String(Base64.decode(data),charset);
  }
  public static void main(String[] args) throws Exception {
    String data = "我们";
    String data1=encode(data,"Unicode");
    String data2=decode(data1,"Unicode");
    System.out.println(data);
    System.out.println(data1);
    System.out.println(data2);
  }
}

附二:JavaScript中Base64编码

?
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<html>
<head>
<title>base64 Encoding/Decoding</title>
</head>
<script type="text/javascript"><!--
var keyStr = "ABCDEFGHIJKLMNOP" +
       "QRSTUVWXYZabcdef" +
       "ghijklmnopqrstuv" +
       "wxyz0123456789+/" +
       "=";
 
function encode64(input) {
  input = unicodetoBytes(input);
  var output = "";
  var chr1, chr2, chr3 = "";
  var enc1, enc2, enc3, enc4 = "";
  var i = 0;
 
  do {
   chr1 = input[i++];
   chr2 = input[i++];
   chr3 = input[i++];
 
   enc1 = chr1 >> 2;
   enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
   enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
   enc4 = chr3 & 63;
 
   if (isNaN(chr2)) {
     enc3 = enc4 = 64;
   } else if (isNaN(chr3)) {
     enc4 = 64;
   }
 
   output = output +
     keyStr.charAt(enc1) +
     keyStr.charAt(enc2) +
     keyStr.charAt(enc3) +
     keyStr.charAt(enc4);
   chr1 = chr2 = chr3 = "";
   enc1 = enc2 = enc3 = enc4 = "";
  } while (i < input.length);
 
  return output;
}
 
function decode64(input) {
  var output = "";
  var chr1, chr2, chr3 = "";
  var enc1, enc2, enc3, enc4 = "";
  var i = 0;
 
  // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
  var base64test = /[^A-Za-z0-9/+///=]/g;
  if (base64test.exec(input)) {
   alert("There were invalid base64 characters in the input text./n" +
      "Valid base64 characters are A-Z, a-z, 0-9, '+', '/', and '='/n" +
      "Expect errors in decoding.");
  }
  input = input.replace(/[^A-Za-z0-9/+///=]/g, "");
  output=new Array();
  do {
   enc1 = keyStr.indexOf(input.charAt(i++));
   enc2 = keyStr.indexOf(input.charAt(i++));
   enc3 = keyStr.indexOf(input.charAt(i++));
   enc4 = keyStr.indexOf(input.charAt(i++));
 
   chr1 = (enc1 << 2) | (enc2 >> 4);
   chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
   chr3 = ((enc3 & 3) << 6) | enc4;
 
   output.push(chr1);
   if (enc3 != 64) {
     output.push(chr2);
   }
   if (enc4 != 64) {
     output.push(chr3);
   }
 
   chr1 = chr2 = chr3 = "";
   enc1 = enc2 = enc3 = enc4 = "";
 
  } while (i < input.length);
  return bytesToUnicode(output);
}
 
 function unicodetoBytes(s)
  {
   var result=new Array();
   if(s==null || s=="") return result;
   result.push(255); // add "FE" to head
   result.push(254);
   for(var i=0;i<s.length;i++)
   {
    var c=s.charCodeAt(i).toString(16);
    if(c.length==1) i="000"+c;
    else if(c.length==2) c="00"+c;
    else if(c.length==3) c="0"+c;
    var var1=parseInt( c.substring(2),16);
    var var2=parseInt( c.substring(0,2),16);
    result.push( var1);
    result.push(var2) ;
   }
   return result;
  }
 
  function bytesToUnicode(bs)
  {
   var result="";
   var offset=0;
   if(bs.length>=2 && bs[0]==255 && bs[1]==254) offset=2; // delete "FE"
   for(var i=offset;i<bs.length;i+=2)
   {
      var code=bs[i]+(bs[i+1]<<8);
      result+=String.fromCharCode(code);
   }
   return result;
  }
//-->
</script>
<body>
<form name="base64Form">
  Type in the message you want to encode in base64, or paste<br>
  base64 encoded text into the text field, select Encode or Decode, <br>
  and click the button!<br>
 
  <textarea name="theText" cols="40" rows="6"></textarea><br>
 
  <input type="button" name="encode" value="Encode to base64"
   onClick="document.base64Form.theText.value=encode64(document.base64Form.theText.value);">
  <input type="button" name="decode" value="Decode from base64"
   onClick="document.base64Form.theText.value=decode64(document.base64Form.theText.value);">
</form>
</body>
</html>

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!