轉 http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html
我們的圖片大部分都是可以轉換成base64編碼的data:image。 這個在將canvas保存爲img的時候尤其有用。雖然除ie外,大部分現代瀏覽器都已經支持原生的基於base64的encode和decode,例如btoa和atob。(將canvas畫布保存成img並強制改變mimetype進行下載,會在下一篇記錄)
但是處於好奇心,還是驅使我去了解下base64編碼的原理。以便也在不支持原生base64編碼的ie下可以得以實現。
【Base64】
-base64的編碼都是按字符串長度,以每3個8bit的字符爲一組,
-然後針對每組,首先獲取每個字符的ASCII編碼,
-然後將ASCII編碼轉換成8bit的二進制,得到一組3*8=24bit的字節
-然後再將這24bit劃分爲4個6bit的字節,並在每個6bit的字節前面都填兩個高位0,得到4個8bit的字節
-然後將這4個8bit的字節轉換成10進制,對照Base64編碼表 (下表),得到對應編碼後的字符。
(注:1. 要求被編碼字符是8bit的,所以須在ASCII編碼範圍內,\u0000-\u00ff,中文就不行。
2. 如果被編碼字符長度不是3的倍數的時候,則都用0代替,對應的輸出字符爲=)
Base64 編碼表 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
Value | Char | Value | Char | Value | Char | Value | Char | |||
0 | A | 16 | Q | 32 | g | 48 | w | |||
1 | B | 17 | R | 33 | h | 49 | x | |||
2 | C | 18 | S | 34 | i | 50 | y | |||
3 | D | 19 | T | 35 | j | 51 | z | |||
4 | E | 20 | U | 36 | k | 52 | 0 | |||
5 | F | 21 | V | 37 | l | 53 | 1 | |||
6 | G | 22 | W | 38 | m | 54 | 2 | |||
7 | H | 23 | X | 39 | n | 55 | 3 | |||
8 | I | 24 | Y | 40 | o | 56 | 4 | |||
9 | J | 25 | Z | 41 | p | 57 | 5 | |||
10 | K | 26 | a | 42 | q | 58 | 6 | |||
11 | L | 27 | b | 43 | r | 59 | 7 | |||
12 | M | 28 | c | 44 | s | 60 | 8 | |||
13 | N | 29 | d | 45 | t | 61 | 9 | |||
14 | O | 30 | e | 46 | u | 62 | + | |||
15 | P | 31 | f | 47 | v | 63 | / |
比如舉下面2個例子:
a) 字符長度爲能被3整除時:比如“Tom” :
T o m ASCII: 84 111 109 8bit字節: 01010100 01101111 01101101 6bit字節: 010101 000110 111101 101101 十進制: 21 6 61 45 對應編碼: V G 9 t
所以,btoa('Tom') = VG9t
b) 字符串長度不能被3整除時,比如“Lucy”: (如果被編碼字符長度不是3的倍數的時候,則都用0代替,對應的輸出字符爲=)
L u c y ASCII: 76 117 99 121 8bit字節: 01001100 01110101 01100011 01111001 00000000 00000000 (補足3的倍數 x*3*8=y*4*6) 6bit字節: 010011 000111 010101 100011 011110 010000 000000 000000 十進制: 19 7 21 35 30 16 (異常) (異常) 對應編碼: T H V j e Q = =
由於Lucy只有4個字母,所以按3個一組的話,第二組還有兩個空位,所以需要用0來補齊。這裏就需要注意,因爲是需要補齊而出現的0,所以轉化成十進制的時候就不能按常規用base64編碼表來對應,所以不是a, 可以理解成爲一種特殊的“異常”,編碼應該對應“=”。
有了上面的理論,那我們實現一個base64編碼就容易了。
轉 http://chhj-292.iteye.com/blog/379700
package com.******.framework.util.encrpytion;
/**
*
* @author
* @version
*/
public final class Base64 implements IEncrypt {
/**
* 標準base64編碼表
*/
private final static String CODEC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
private final static Base64 base64 = new Base64();
private Base64() {
}
public static Base64 getInstance() {
return base64;
}
/* (non-Javadoc)
* @see com.pantosoft.framework.util.encrpytion.IEncryptencrypt(java.lang.String)
*/
public String encrypt(String s) throws Exception {
return encode(s.getBytes());
}
/* (non-Javadoc)
* @see com.pantosoft.framework.util.encrpytion.IEncrypt#decrypt(java.lang.String)
*/
public String decrypt(String s) throws Exception {
return new String(this.decode(s));
}
private String encode(byte[] bytes) {
StringBuilder s = new StringBuilder();
int i = 0;
byte pos;
/*
一次處理3個字節,3*8 == 4*6 的運算規則來進行重新編碼
該方法中的*&63,*&15,*&3操作的意義如下:
計算機中byte數據類型存儲的64形式如下:11111111
計算機中byte數據類型存儲的15形式如下:1111,即 2^3 + 2^2 + 2^1 + 2^0 = 15
“&”、“與”,運算這裏主要進行高位清零操作。
*/
for (i = 0; i < (bytes.length - (bytes.length % 3)); i += 3) {
//第一個字節,根據源字節的第一個字節處理。
//規則:源第一字節右移兩位,去掉低2位,高2位補零。
//既:00 + 高6位
pos = (byte) ((bytes[i] >> 2) & 63);
s.append(CODEC.charAt(pos));
//第二個字節,根據源字節的第一個字節和第二個字節聯合處理。
//規則如下,第一個字節高6位去掉左移四位,第二個字節右移四位
//即:源第一字節低2位 + 源第2字節高4位
pos = (byte) (((bytes[i] & 3) << 4) + ((bytes[i + 1] >> 4) & 15));
s.append(CODEC.charAt(pos));
//第三個字節,根據源字節的第二個字節和第三個字節聯合處理,
//規則第二個字節去掉高4位並左移兩位(得高6位),第三個字節右移6位並去掉高6位(得低2位),相加即可
pos = (byte) (((bytes[i + 1] & 15) << 2) + ((bytes[i + 2] >> 6) & 3));
s.append(CODEC.charAt(pos));
//第四個字節,規則,源第三字節去掉高2位即可
pos = (byte) (((bytes[i + 2]) & 63));
s.append(CODEC.charAt(pos));
//根據base64的編碼規則,每76個字符需要一個換行
//76*3/4 = 57
if (((i + 2) % 56) == 0) {
s.append("\r\n");
}
}
if (bytes.length % 3 != 0) {
if (bytes.length % 3 == 2) {
pos = (byte) ((bytes[i] >> 2) & 63);
s.append(CODEC.charAt(pos));
pos = (byte) (((bytes[i] & 3) << 4) + ((bytes[i + 1] >> 4) & 15));
s.append(CODEC.charAt(pos));
pos = (byte) ((bytes[i + 1] & 15) << 2);
s.append(CODEC.charAt(pos));
s.append("=");
} else if (bytes.length % 3 == 1) {
//分出第一個二進制位的前6位,右移兩位,得到一個新8位
pos = (byte) ((bytes[i] >> 2) & 63);
s.append(CODEC.charAt(pos));
//先清零比3高的高位,分出8位的後兩位,然後左移4位,得到一個新8位
pos = (byte) ((bytes[i] & 3) << 4);
s.append(CODEC.charAt(pos));
s.append("==");
}
}
return s.toString();
}
/**
*
* @param s
* @return
* @throws Exception
*/
public byte[] decode(String s) throws Exception {
StringBuffer buf = new StringBuffer(s);
int i = 0;
char c = ' ';
char oc = ' ';
while (i < buf.length()) {
oc = c;
c = buf.charAt(i);
if (oc == '\r' && c == '\n') {
buf.deleteCharAt(i);
buf.deleteCharAt(i - 1);
i -= 2;
} else if (c == '\t') {
buf.deleteCharAt(i);
i--;
} else if (c == ' ') {
i--;
}
i++;
}
//base64編碼的字符長度必須爲4的倍數
if (buf.length() % 4 != 0) {
throw new Exception("Base64 decoding invalid length");
}
//預設的字節數組的長度
byte[] bytes = new byte[3 * (buf.length() / 4)];
int index = 0;
/**
* 每4個base64字符代表一個源字符編碼後的字符!
*
* 然後每四個字符分別做循環,每個循環左移6位,作爲低6位,該低6位再補上下一個base64字符在base64碼錶中的序列。
* 因爲字符在碼錶中的序列小於等於64,即,小於等於2的6次方(6位)!
*/
for (i = 0; i < buf.length(); i += 4) {
byte base64Index = 0;
int nGroup = 0;
for (int j = 0; j < 4; j++) {
char theChar = buf.charAt(i + j);
if (theChar == '=') {
base64Index = 0;
} else {
base64Index = getBase64Index(theChar);
}
if (base64Index == -1) {
throw new Exception("Base64 decoding bad character");
}
//每次都想高位移動6個位置後再加上新的字符所在base64編碼表中的位置。
nGroup = (nGroup << 6) + base64Index;
}
//右移16位,取高8位
bytes[index] = (byte) (255 & (nGroup >> 16));
index++;
//右移8位,取高16位,且與00..0011111111(32位windows系統)進行and操作,取該高16位的低8位。
if ((255 & (nGroup >> 8)) == 0) {
continue;
}
bytes[index] = (byte) (255 & (nGroup >> 8));
index++;
//直接與00..0011111111進行and操作,該32位數的低8位
if ((255 & nGroup) == 0) {
continue;
}
bytes[index] = (byte) (255 & (nGroup));
index++;
}
byte[] newBytes = new byte[index];
for (i = 0; i < index; i++) {
newBytes[i] = bytes[i];
}
return newBytes;
}
/**
* 從編碼表中找出對應的字符序列
* @param c
* @return
*/
private byte getBase64Index(char c) {
byte index = -1;
for (byte i = 0, j = (byte) (CODEC.length() & 225); i < j; i++) {
if (CODEC.charAt(i) == c) {
index = i;
break;
}
}
return index;
}
static final char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z' };
private static String toUnsignedString(int i, int j) {
char ac[] = new char[32];
int k = 32;
int l = 1 << j;
int i1 = l - 1;
do {
ac[--k] = digits[i & i1];
i >>>= j;
} while (i != 0);
return new String(ac, k, 32 - k);
}
public static void main(String[] args) {
String encodedString = null;
String s = " linux 和 windows下默認使用firefox下載文件的時侯,rar文件會自動被firefox下載後顯示爲一堆";
byte[] bytes = s.getBytes();
for (int i = 0; i < bytes.length; i++) {
String s1 = toUnsignedString(bytes[i], 1);
if (s1.length() >= 24)
System.out.println(s1 + "," + s1.substring(24, s1.length()));
}
try {
encodedString = Base64.getInstance().encrypt(s);
System.out.println("加密:" + encodedString);
System.out.println("解密:"
+ Base64.getInstance().decrypt(encodedString));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}