关于base64编码的原理及实现

1 篇文章 0 订阅

转  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
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

比如举下面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();
		}
	}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值