关于base64编码的原理及实现
2014-05-26 17:21
393 查看
转 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代替,对应的输出字符为=)
比如举下面2个例子:
a) 字符长度为能被3整除时:比如“Tom” :
所以,btoa('Tom') = VG9t
b) 字符串长度不能被3整除时,比如“Lucy”: (如果被编码字符长度不是3的倍数的时候,则都用0代替,对应的输出字符为=)
由于Lucy只有4个字母,所以按3个一组的话,第二组还有两个空位,所以需要用0来补齐。这里就需要注意,因为是需要补齐而出现的0,所以转化成十进制的时候就不能按常规用base64编码表来对应,所以不是a,
可以理解成为一种特殊的“异常”,编码应该对应“=”。
有了上面的理论,那我们实现一个base64编码就容易了。
转 http://chhj-292.iteye.com/blog/379700
我们的图片大部分都是可以转换成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 | / |
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(); } } }
相关文章推荐
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理和实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理和实现
- 关于DataURI base64编码的原理及实现
- 关于base64编码的原理及实现
- [编解码] 关于base64编码的原理及实现
- 关于base64编码的原理及实现
- 关于base64编码的原理及实现--for SMTP
- 关于base64编码的原理及实现