Java 和 JavaScript 真正通用的Base64编码详解
Java和JavaScriptBase64编码
在开发Java Web应用的时候,可能会在服务器端用Java做Base64编码,而在客户端用JavaScript进行解码。这样就要求两边的Base64编码机制保持一致。
使用Base64编码,可能会碰到各种奇怪情况,甚至怀疑编码有bug。但实际上不是这样的。Base64理论上操作的对象不是字符串而是字节数组。它的原理就是把ASCII码的255个字符缩小到用64个来表示。具体就是原来三个字节用四个字节表示,编码后长度有一定的增长。
1)最好一次编码,避免分段编码,确实要分段编码,每一段字节数应该是3的倍数。
长字节流,如果要边读取边编码,每一段必须是3的倍数,否则就可能在还原的时候出乱。一般人喜欢用2的乘方来定义数组,例如byte[1024],因为不是3的倍数,可能还原时出错。正确的例子是:
byte[]bs=newbyte[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编码
packagewebsharp.util; publicclassBase64{ privatestaticfinalbyte[]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)'/' }; privatestaticfinalbyte[]decodingTable; static{ decodingTable=newbyte[128]; for(inti=0;i<128;i++){ decodingTable[i]=(byte)-1; } for(inti='A';i<='Z';i++){ decodingTable[i]=(byte)(i-'A'); } for(inti='a';i<='z';i++){ decodingTable[i]=(byte)(i-'a'+26); } for(inti='0';i<='9';i++){ decodingTable[i]=(byte)(i-'0'+52); } decodingTable['+']=62; decodingTable['/']=63; } publicstaticbyte[]encode(byte[]data,intoffset){ byte[]bytes; intrealCount=data.length-offset; intmodulus=realCount%3; if(modulus==0){ bytes=newbyte[(4*realCount)/3]; }else{ bytes=newbyte[4*((realCount/3)+1)]; } intdataLength=(data.length-modulus); inta1; inta2; inta3; for(inti=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]; } intb1; intb2; intb3; intd1; intd2; switch(modulus){ case0:/*nothinglefttodo*/ break; case1: 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; case2: 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; } returnbytes; } publicstaticbyte[]decode(byte[]data){ byte[]bytes; byteb1; byteb2; byteb3; byteb4; data=discardNonBase64Bytes(data); if(data[data.length-2]=='='){ bytes=newbyte[(((data.length/4)-1)*3)+1]; }elseif(data[data.length-1]=='='){ bytes=newbyte[(((data.length/4)-1)*3)+2]; }else{ bytes=newbyte[((data.length/4)*3)]; } for(inti=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)); }elseif(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); } returnbytes; } publicstaticbyte[]decode(Stringdata){ byte[]bytes; byteb1; byteb2; byteb3; byteb4; data=discardNonBase64Chars(data); if(data.charAt(data.length()-2)=='='){ bytes=newbyte[(((data.length()/4)-1)*3)+1]; }elseif(data.charAt(data.length()-1)=='='){ bytes=newbyte[(((data.length()/4)-1)*3)+2]; }else{ bytes=newbyte[((data.length()/4)*3)]; } for(inti=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)); }elseif(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(inti=0;i<bytes.length;i++)System.out.println(","+bytes[i]); returnbytes; } privatestaticbyte[]discardNonBase64Bytes(byte[]data){ byte[]temp=newbyte[data.length]; intbytesCopied=0; for(inti=0;i<data.length;i++){ if(isValidBase64Byte(data[i])){ temp[bytesCopied++]=data[i]; } } byte[]newData=newbyte[bytesCopied]; System.arraycopy(temp,0,newData,0,bytesCopied); returnnewData; } privatestaticStringdiscardNonBase64Chars(Stringdata){ StringBuffersb=newStringBuffer(); intlength=data.length(); for(inti=0;i<length;i++){ if(isValidBase64Byte((byte)(data.charAt(i)))){ sb.append(data.charAt(i)); } } returnsb.toString(); } privatestaticbooleanisValidBase64Byte(byteb){ if(b=='='){ returntrue; }elseif((b<0)||(b>=128)){ returnfalse; }elseif(decodingTable[b]==-1){ returnfalse; } returntrue; } publicstaticStringencode(Stringdata,Stringcharset)throwsException { //byte[]result=(data.getBytes("Unicode")); if(data==null||data.length()==0)returndata; intoffset=0; //getBytes("unicode")转完后会在前头加上两字节”FE“ byte[]result=encode(data.getBytes(charset),offset); StringBuffersb=newStringBuffer(result.length); for(inti=0;i<result.length;i++)sb.append((char)result[i]); returnsb.toString(); } publicstaticStringdecode(Stringdata,Stringcharset)throwsException { if(data==null||data.length()==0)returndata; returnnewString(Base64.decode(data),charset); } publicstaticvoidmain(String[]args)throwsException{ Stringdata="我们"; Stringdata1=encode(data,"Unicode"); Stringdata2=decode(data1,"Unicode"); System.out.println(data); System.out.println(data1); System.out.println(data2); } }
附二:JavaScript中Base64编码
<html> <head> <title>base64Encoding/Decoding</title> </head> <scripttype="text/javascript"><!-- varkeyStr="ABCDEFGHIJKLMNOP"+ "QRSTUVWXYZabcdef"+ "ghijklmnopqrstuv"+ "wxyz0123456789+/"+ "="; functionencode64(input){ input=unicodetoBytes(input); varoutput=""; varchr1,chr2,chr3=""; varenc1,enc2,enc3,enc4=""; vari=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; }elseif(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); returnoutput; } functiondecode64(input){ varoutput=""; varchr1,chr2,chr3=""; varenc1,enc2,enc3,enc4=""; vari=0; //removeallcharactersthatarenotA-Z,a-z,0-9,+,/,or= varbase64test=/[^A-Za-z0-9/+///=]/g; if(base64test.exec(input)){ alert("Therewereinvalidbase64charactersintheinputtext./n"+ "Validbase64charactersareA-Z,a-z,0-9,'+','/',and'='/n"+ "Expecterrorsindecoding."); } input=input.replace(/[^A-Za-z0-9/+///=]/g,""); output=newArray(); 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); returnbytesToUnicode(output); } functionunicodetoBytes(s) { varresult=newArray(); if(s==null||s=="")returnresult; result.push(255);//add"FE"tohead result.push(254); for(vari=0;i<s.length;i++) { varc=s.charCodeAt(i).toString(16); if(c.length==1)i="000"+c; elseif(c.length==2)c="00"+c; elseif(c.length==3)c="0"+c; varvar1=parseInt(c.substring(2),16); varvar2=parseInt(c.substring(0,2),16); result.push(var1); result.push(var2); } returnresult; } functionbytesToUnicode(bs) { varresult=""; varoffset=0; if(bs.length>=2&&bs[0]==255&&bs[1]==254)offset=2;//delete"FE" for(vari=offset;i<bs.length;i+=2) { varcode=bs[i]+(bs[i+1]<<8); result+=String.fromCharCode(code); } returnresult; } //--> </script> <body> <formname="base64Form"> Typeinthemessageyouwanttoencodeinbase64,orpaste<br> base64encodedtextintothetextfield,selectEncodeorDecode,<br> andclickthebutton!<br> <textareaname="theText"cols="40"rows="6"></textarea><br> <inputtype="button"name="encode"value="Encodetobase64" onClick="document.base64Form.theText.value=encode64(document.base64Form.theText.value);"> <inputtype="button"name="decode"value="Decodefrombase64" onClick="document.base64Form.theText.value=decode64(document.base64Form.theText.value);"> </form> </body> </html>
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!