基本概念

Base64这个术语最初是在“MIME内容传输编码规范”中提出的。Base64不是一种加密算法,虽然编码后的字符串看起来有点加密的赶脚。它实际上是一种“二进制到文本”的编码方法,它能够将给定的任意二进制数据转换(映射)为ASCII字符串的形式,以便在只支持文本的环境中也能够顺利地传输二进制数据。例如支持MIME的电子邮件应用,或需要在XML中存储复杂数据(例如图片)时。
 
要实现Base64,首先需要选取适当的64个字符组成字符集。一条通用的原则是从某种常用字符集中选取64个可打印字符,这样就能避免在传输过程中丢失数据(不可打印字符在传输过程中可能会被当做特殊字符处理,从而导致丢失)。例如,MIME的Base64实现选用了大写字母、小写字母和0~9的数字作为前62个字符。其他实现通常会沿用MIME的这种方式,而仅仅在最后2个字符上有所不同,例如UTF-7编码。
  

一个例子

下面这段文本:
Man is distinguished, not only by his reason, but by this singular passion from
other animals, which is a lust of the mind, that by a perseverance of delight
in the continued and indefatigable generation of knowledge, exceeds the short
vehemence of any carnal pleasure.
通过MIME Base64进行转换后就成为:
TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=

转换方法

以例子开头的“Man”被转换为“TWFu”为例,我们来看看Base64基本的转换过程:
  1. M、a和n的ASCII编码分别为01001101、01100001和01101110,合并后得到一个24位的二进制串010011010110000101101110
  2. 按每6位一组将其分为4组:010011、010110、000101、101110
  3. 最后按对应关系从字符集中取出4个字符(即T、W、F、u)作为结果(本文后面列出了由MIME定义的字符集)。
Base64的基本思想就是这么简单:它将每3个字节(24位)转换为4个字符。因为6位二进制数可以表示64个不同的数,因此只要确定了字符集(含64个字符),并为其中的每个字符确定一个唯一的编码,就可以通过正向与反向映射将二进制字节转换为Base64编码或反之。

补零处理

通过不断将每3个字节转换为4个Base64字符之后,最后可能会出现以下3种情况之一:
  1. 没有字节剩下
  2. 还剩下1个字节
  3. 还剩下2个字节
1没什么好说的。后面的2和3该如何处理呢?
遇到这种情况,就需要在剩下的字节后面补零,直到其位数能够被6整除(因为Base64是对每6位进行编码的)。假如还剩下1个字节,即8位,那么需要再补4个0使其成为12位,这样就可以分为2组了;如果剩下2个字节,即16位,那么只需要再补2个0(18位)就可以分成3组了。最后再用普通方法做映射即可。

填充

还原时,依次将每4个字符还原成3个字节,最后会出现3种情况之一:
  1. 没有字符剩下
  2. 还剩下2个字符
  3. 还剩下3个字符
这3种情况与上面的3种情况一一对应,只要对补零的过程反过来处理,就可以原样还原了。
我们经常会在Base64编码字符串中看到最后有“=”字符,这就是通过填充生成的。填充就是当出现编码时的情况2和3时,在后面补上“=”字符,使编码后的字符数为4的倍数。 
所以我们可以很容易地想到,情况2,即还剩下1个字节时,需要补2个“=”,因为此时最后一个字节编码为2个字符,补上2个“=”正好凑够4个。情况3同理,需要补1个“=”。 
填充不是必须的,因为无需填充也可以通过编码后的内容计算出缺失的字节。所以在一些实现中填充是必须的,有些却不是。一种必须使用填充的场合是当需要将多个Base64编码文件合并为一个文件的时候。
 

实现(示例)

下面是一个Base64字符集,它包含大写字母、小写字母和数字,以及“+”和“/”符号。
 
编码 字符   编码 字符   编码 字符   编码 字符
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 /
 
利用这个字符集我们可以写一个简单的Base64实现(本文最后附有完整源代码):
下面这个encode()方法用来将Java字符串转换为字节数组(Base64操作的是字节),然后调用真正的encode()方法完成编码:
public String encode(String inputStr, String charset, boolean padding)
throws UnsupportedEncodingException {
String encodeStr = null; byte[] bytes = inputStr.getBytes(charset);
encodeStr = encode(bytes, padding); return encodeStr;
}

encode()方法的核心代码是:

for (int i = 0; i < groups; i++) {
byte_1 = bytes[3*i] & 0xFF;
byte_2 = bytes[3*i+1] & 0xFF;
byte_3 = bytes[3*i+2] & 0xFF; group_6bit_1 = byte_1 >>> 2;
group_6bit_2 = (byte_1 & 0x03) << 4 | byte_2 >>> 4;
group_6bit_3 = (byte_2 & 0x0F) << 2 | byte_3 >>> 6;
group_6bit_4 = byte_3 & 0x3F; sb.append(CHARSET[group_6bit_1])
.append(CHARSET[group_6bit_2])
.append(CHARSET[group_6bit_3])
.append(CHARSET[group_6bit_4]);
}
即将每3个字节转换为4个字符。
当然还需要判断最后是否还有剩余的字节,如果有要单独处理:
if (tail == 1) {
byte_1 = bytes[bytes.length-1] & 0xFF; group_6bit_1 = byte_1 >>> 2;
group_6bit_2 = (byte_1 & 0x03) << 4; sb.append(CHARSET[group_6bit_1])
.append(CHARSET[group_6bit_2]); if (padding) {
sb.append('=').append('=');
}
} else if (tail == 2) {
byte_1 = bytes[bytes.length-2] & 0xFF;
byte_2 = bytes[bytes.length-1] & 0xFF; group_6bit_1 = byte_1 >>> 2;
group_6bit_2 = (byte_1 & 0x03) << 4 | byte_2 >>> 4;
group_6bit_3 = (byte_2 & 0x0F) << 2; sb.append(CHARSET[group_6bit_1])
.append(CHARSET[group_6bit_2])
.append(CHARSET[group_6bit_3]); if (padding) {
sb.append('=');
}
}

decode过程是类似的,具体请自行查阅完整代码。

附:源程序
package base64;

import java.io.UnsupportedEncodingException;

/**
* This class provides a simple implementation of Base64 encoding and decoding.
*
* @author QiaoMingkui
*
*/
public class Base64 {
/*
* charset
*/
private static final char[] CHARSET = {
'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', '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', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
}; /*
* charset used to decode.
*/
private static final int[] DECODE_CHARSET = new int[128];
static {
for (int i=0; i<64; i++) {
DECODE_CHARSET[CHARSET[i]] = i;
}
} /**
* A convenient method for encoding Java String,
* it uses encode(byte[], boolean) to encode byte array.
*
* @param inputStr a string to be encoded.
* @param charset charset name ("GBK" for example) that is used to convert inputStr into byte array.
* @param padding whether using padding characters "="
* @return encoded string
* @throws UnsupportedEncodingException if charset is unsupported
*/
public String encode(String inputStr, String charset, boolean padding)
throws UnsupportedEncodingException {
String encodeStr = null; byte[] bytes = inputStr.getBytes(charset);
encodeStr = encode(bytes, padding); return encodeStr;
} /**
* Using Base64 to encode bytes.
*
* @param bytes byte array to be encoded
* @param padding whether using padding characters "="
* @return encoded string
*/
public String encode(byte[] bytes, boolean padding) {
// 4 6-bit groups
int group_6bit_1,
group_6bit_2,
group_6bit_3,
group_6bit_4; // bytes of a group
int byte_1,
byte_2,
byte_3; // number of 3-byte groups
int groups = bytes.length / 3;
// at last, there might be 0, 1, or 2 byte(s) remained,
// which needs to be encoded individually.
int tail = bytes.length % 3; StringBuilder sb = new StringBuilder(groups * 4 + 4); // handle each 3-byte group
for (int i = 0; i < groups; i++) {
byte_1 = bytes[3*i] & 0xFF;
byte_2 = bytes[3*i+1] & 0xFF;
byte_3 = bytes[3*i+2] & 0xFF; group_6bit_1 = byte_1 >>> 2;
group_6bit_2 = (byte_1 & 0x03) << 4 | byte_2 >>> 4;
group_6bit_3 = (byte_2 & 0x0F) << 2 | byte_3 >>> 6;
group_6bit_4 = byte_3 & 0x3F; sb.append(CHARSET[group_6bit_1])
.append(CHARSET[group_6bit_2])
.append(CHARSET[group_6bit_3])
.append(CHARSET[group_6bit_4]);
} // handle last 1 or 2 byte(s)
if (tail == 1) {
byte_1 = bytes[bytes.length-1] & 0xFF; group_6bit_1 = byte_1 >>> 2;
group_6bit_2 = (byte_1 & 0x03) << 4; sb.append(CHARSET[group_6bit_1])
.append(CHARSET[group_6bit_2]); if (padding) {
sb.append('=').append('=');
}
} else if (tail == 2) {
byte_1 = bytes[bytes.length-2] & 0xFF;
byte_2 = bytes[bytes.length-1] & 0xFF; group_6bit_1 = byte_1 >>> 2;
group_6bit_2 = (byte_1 & 0x03) << 4 | byte_2 >>> 4;
group_6bit_3 = (byte_2 & 0x0F) << 2; sb.append(CHARSET[group_6bit_1])
.append(CHARSET[group_6bit_2])
.append(CHARSET[group_6bit_3]); if (padding) {
sb.append('=');
}
} return sb.toString();
} /**
* Decode a Base64 string to bytes (byte array).
*
* @param code Base64 string to be decoded
* @return byte array
*/
public byte[] decode(String code) {
char[] chars = code.toCharArray(); int group_6bit_1,
group_6bit_2,
group_6bit_3,
group_6bit_4; int byte_1,
byte_2,
byte_3; int len = chars.length;
// ignore last '='s
if (chars[chars.length - 1] == '=') {
len--;
}
if (chars[chars.length - 2] == '=') {
len--;
} int groups = len / 4;
int tail = len % 4; // each group of characters (4 characters) will be converted into 3 bytes,
// and last 2 or 3 characters will be converted into 1 or 2 byte(s).
byte[] bytes = new byte[groups * 3 + (tail > 0 ? tail - 1 : 0)]; int byteIdx = 0; // decode each group
for (int i=0; i<groups; i++) {
group_6bit_1 = DECODE_CHARSET[chars[4*i]];
group_6bit_2 = DECODE_CHARSET[chars[4*i + 1]];
group_6bit_3 = DECODE_CHARSET[chars[4*i + 2]];
group_6bit_4 = DECODE_CHARSET[chars[4*i + 3]]; byte_1 = group_6bit_1 << 2 | group_6bit_2 >>> 4;
byte_2 = (group_6bit_2 & 0x0F) << 4 | group_6bit_3 >>> 2;
byte_3 = (group_6bit_3 & 0x03) << 6 | group_6bit_4; bytes[byteIdx++] = (byte) byte_1;
bytes[byteIdx++] = (byte) byte_2;
bytes[byteIdx++] = (byte) byte_3;
} // decode last 2 or 3 characters
if (tail == 2) {
group_6bit_1 = DECODE_CHARSET[chars[len - 2]];
group_6bit_2 = DECODE_CHARSET[chars[len - 1]]; byte_1 = group_6bit_1 << 2 | group_6bit_2 >>> 4;
bytes[byteIdx] = (byte) byte_1;
} else if (tail == 3) {
group_6bit_1 = DECODE_CHARSET[chars[len - 3]];
group_6bit_2 = DECODE_CHARSET[chars[len - 2]];
group_6bit_3 = DECODE_CHARSET[chars[len - 1]]; byte_1 = group_6bit_1 << 2 | group_6bit_2 >>> 4;
byte_2 = (group_6bit_2 & 0x0F) << 4 | group_6bit_3 >>> 2; bytes[byteIdx++] = (byte) byte_1;
bytes[byteIdx] = (byte) byte_2;
} return bytes;
} /**
* Test.
* @param args
*/
public static void main(String[] args) {
Base64 base64 = new Base64();
String str = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
System.out.println(str);
try {
String encodeStr = base64.encode(str, "GBK", false);
System.out.println(encodeStr);
byte[] decodeBytes = base64.decode(encodeStr);
String decodeStr = new String(decodeBytes, "GBK");
System.out.println(decodeStr);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}

工具类

可以使用Java现有的工具类进行编码解码,这样可以简化代码。

        BASE64Encoder encoder = new BASE64Encoder();
BASE64Decoder decoder = new BASE64Decoder();
String str1 = encoder.encode("111".getBytes());
String str2 = encoder.encode("222".getBytes());
System.out.println(str1);
System.out.println(new String(decoder.decodeBuffer(str1)));
System.out.println(str2);
System.out.println(new String(decoder.decodeBuffer(str2)));

运行结果:

MTEx

MjIy

转载自:

Base64编码简介

 
 

JavaWeb—Base64编码(转载)的更多相关文章

  1. NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计算字符串长度,汉字算两个字符 2019周笔记(2.18-2.23) Mysql语句中当前时间不能直接使用C#中的Date.Now传输 Mysql中Count函数的正确使用

    NET MVC全局异常处理(一)   目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关 ...

  2. (转载)javascript将base64编码的图片数据转换为file并提交

    /** * @param base64Codes * 图片的base64编码 */ function sumitImageFile(base64Codes){ var form=document.fo ...

  3. 一篇文章彻底弄懂Base64编码原理(转载)

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...

  4. Base64编码

    Base64编码 写在前面 今天在做一个Android app时遇到了一个问题:Android端采用ASE对称加密的数据在JavaWeb(jre1.8.0_7)后台解密时,居然解密失败了!经过测试后发 ...

  5. 浅析用Base64编码的图片优化网页加载速度

    想必大家都知道网页加载的过程,从开始请求,到加载页面,开始解析和显示网页,遇到图片就再次向服务器发送请求,加载图片.如果图片很多的话,就会产生大量的http请求,从而影响页面的加载速度.所以现在有一种 ...

  6. [编解码] 关于base64编码的原理及实现

    转载自: http://www.cnblogs.com/hongru/archive/2012/01/14/2321397.html [Base64]-base64的编码都是按字符串长度,以每3个8b ...

  7. Base64编码和解码算法

    Base64么新鲜的算法了.只是假设你没从事过页面开发(或者说动态页面开发.尤其是邮箱服务),你都不怎么了解过,仅仅是听起来非常熟悉. 对于黑客来说,Base64与MD5算法有着相同的位置.由于电子邮 ...

  8. java使用Base64编码和解码的图像文件

    1.编码和解码下面的代码示例看: import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import j ...

  9. Base64编码 概念和用途

    Base64概念 什么是Base64? 依照RFC2045的定义,Base64被定义为:Base64内容传送编码被设计用来把随意序列的8位字节描写叙述为一种不易被人直接识别的形式.(The Base6 ...

随机推荐

  1. C/C++中printf和C++中cout的输出格式

    一. Printf 输出格式 C中格式字符串的一般形式为: %[标志][输出最小宽度][.精度][长度]类型,其中方括号[]中的项为可选项.各项的意义介绍如下:1.类型类型字符用以表示输出数据的类型, ...

  2. hdu4675 GCD of Sequence 莫比乌斯+组合数学

    /** 题目:hdu4675 GCD of Sequence 链接:http://acm.hdu.edu.cn/showproblem.php?pid=4675 题意:给定n个数的a数组,以及m,k: ...

  3. Tomcat热部署及错误排查

    Maven的热部署 第一步:配置Tomcat的登陆的用户名与密码 C:\apache-tomcat-7.0.33\conf\ tomcat-users.xml  从第36行开始配置     <r ...

  4. java访问属性

  5. ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点

    LinkedList接口(在代码的使用过程中和ArrayList没有什么区别) ArrayList底层是object数组,所以ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点. 而在L ...

  6. LodRunner实现大负载测试的四部曲(配置系统参数、配置LR、修改脚本、设置组策略)

    见 http://www.51testing.com/?uid-97659-action-viewspace-itemid-210924 LoadRunner以下简称(LR)是目前业界最流行的压力测试 ...

  7. 编程之美 set 19 连连看游戏设计

    题目 1. 怎么用简单的计算机模型来描述这个问题 ? 2. 怎么判断两个图像是否能相消 3. 怎么求出相同图形之间的最短距离(转弯数最少)? 4. 怎么确定目前处于死锁状态? 如何设计算法来解除死锁? ...

  8. ios开发之 -- 单例类

    单例模式是一种软件设计模式,再它的核心结构中指包含一个被称为单例类的特殊类. 通过单例模式可以保证系统中一个类只有一个势力而且该势力易于外界访问,从而方便对势力个数的控制并节约系统资源.如果希望在系统 ...

  9. shell脚本学习总结10--系统函数调用

    1.打印出彩色的格式 [root@new sbin]# cat demo.sh #/bin/bash . /etc/init.d/functions read -p "Pleas input ...

  10. 动态加载script文件

    动态加载script文件:   http://www.cnblogs.com/skykang/archive/2011/07/21/2112685.html