当前位置 : 首页 » 文章分类 :  开发  »  Base64

Base64

Base64 编解码笔记

Base64 是一种用 64 个字符来表示任意二进制数据的方法。

RFC4648 介绍了 Base64/32/16 编码规范
https://datatracker.ietf.org/doc/html/rfc4648


为什么要使用Base64?

Base64 编码的作用:由于某些系统中只能使用 ASCII 字符。Base64 就是用来将非 ASCII 字符的数据转换成 ASCII 字符的一种方法。

Base64 是一种很常见的编码规范,其作用是将二进制序列转换为人类可读的 ASCII 字符序列,常用在需用通过文本协议(比如HTTP和SMTP)来传输二进制数据的情况下。Base64 并不是加密解密算法,尽管我们有时也听到使用Base64来加密解密的说法,但这里所说的加密与解密实际是指编码(encode)和解码(decode)的过程,其变换是非常简单的,仅仅能够避免信息被直接识别。


byte[] 类型字段可接收 base64 字符串

SpringBoot 对外暴露 byte[] 类型的字段可以接收 base64 字符串,自动转换为字节数组

public class FeatureExtractRequest {
    @NotEmpty(message = "image cannot be empty!")
    private byte[] image;
}

可以直接在 postman 中如下传参:

{
    "image": "/9j/...."
}

但要注意:

  • base64 字符串长度是 4 的倍数,否则转换会报错
  • 之前好像遇到过 url 编码的 base64 也会报错,spring 默认只支持 basic 编码的 base64 自动转换为 byte[]

Base64使用场景

它的使用场景有很多,比如:
1、将图片等资源文件以Base64编码形式直接放于代码中,使用的时候反Base64后转换成Image对象使用;
2、有些文本协议不支持不可见字符的传递,只能转换成可见字符来传递信息。
3、有时在一些特殊的场合,大多数消息是纯文本的,偶尔需要用这条纯文本通道传一张图片之类的情况发生的时候,就会用到Base64,比如多功能Internet 邮件扩充服务(MIME)就是用Base64对邮件的附件进行编码的。
4、base64 最早就是用来邮件传输协议中的,原因是邮件传输协议只支持 ascii 字符传递,因此如果要传输二进制文件,如:图片、视频是无法实现的。因此 base64 就可以用来将二进制文件内容编码为只包含 ascii 字符的内容,这样就可以传输了


Base64编码过程

Base64 使用 64 个通用的可打印字符来存储和表示二进制字数据,同时也可以进行简单的加密(Base64 编码是可逆的,不是真正的加密),生成不可读文本。

1、准备一个包含 64 个字符的数组作为码表,包含 大小写、数字、加号、斜线
['A', 'B', 'C', ... 'a', 'b', 'c', ... '0', '1', ... '+', '/']

2、二进制数据是以字节为单位的。对二进制数据进行处理,每 3 个字节一组,一共是 3x8=24bit, 划为 4 组,每组正好 6 个 bit, 这样我们得到 4 个 [0,63] 范围内的数字作为索引,然后查表,获得相应的 4 个字符,就是编码后的字符串。

3、如果要编码的二进制数据的字节数不是 3 的倍数,最后会剩下 1 个或 2 个字节,在原始二进制码末尾补 0 直到恰好是 6bits 的倍数,然后查表转换为字符编码,再在最后补 1 到 2 个 = 号,使总的字符个数是 4 的整数倍。

所以,Base64 编码会把 3 字节的二进制数据编码为 4 字节的文本数据,长度增加 33%, 好处是编码后的文本数据可以在邮件正文、网页等直接显示。

例如
1、一字节的二进制数据
0000 0001
1 个字节是 8bits, 后补 4 个 0 得到 12bit,成为 6bit 的整数倍,得到
0000 0001 0000
6bit 一组,共两组 000000 和 010000, 分别是 0 和 16, 查表得
AQ
后补 2 个 = 得 AQ==

3、两字节的二进制数据
0000 0000 0000 0000
2 个字节是 16bit, 后补 2 个 0 得到 18bit,成为 6bit 的整数倍,得到
0000 0000 0000 0000 00
6bit 一组,共三组 000000 000000 000000 分别是 0, 0, 0, 查表得
AAA
后补 1 个 = 得 AAA=

可以发现,每一个 = 代表二进制源码末尾添加了 2bits 的 0, Base64 编码后字串的长度将是 4 的整数倍,根据末尾 = 的个数计算编码过程中补 0 的个数,就可以正常解码

URL safe的Base64编码

由于标准的 Base64 编码后可能出现字符 +/, 在 URL 中就不能直接作为参数,所以又有一种 “url safe” 的 base64 编码,其实就是把字符 + 和 / 分别变成 - 和 _


Java原生的Base64编解码工具类

Java 8 在 java.util 包中新增了 Base64 编解码类,直接就可以用。

java.util.Base64 包括下面三种 Base64 编解码器
1 Basic编码
2 URL编码
3 MIME编码

Basic 编解码

Basic 编码是标准的 BASE64 编码,用于处理常规的需求:输出的内容不添加换行符,而且输出的内容由字母加数字组成。

URL 编解码

由于 URL 对斜线 / 有特殊的意义,因此 URL 编码需要替换掉它,使用下划线 _ 替换。
URL 编解码器和 Basic 的不同就在于会将编码后的结果中的 / 替换为 _

MIME 编解码

MIME 编解码器和 Basic 的不同在于会自动换行,每行不超过 76 个字符,自动用 \r\n 分割以实现换行。但是结果中还是会有 /

@Test
public void testBase64Codec() {
    System.out.println("Basic 编解码");
    String basic = Base64.getEncoder().encodeToString("subjects?abcd".getBytes(StandardCharsets.UTF_8));
    System.out.println(basic);
    System.out.println(new String(Base64.getDecoder().decode(basic), StandardCharsets.UTF_8));

    System.out.println("\nURL 编解码");
    String url = Base64.getUrlEncoder().encodeToString("subjects?abcd".getBytes(StandardCharsets.UTF_8));
    System.out.println(url);
    System.out.println(new String(Base64.getUrlDecoder().decode(url), StandardCharsets.UTF_8));

    System.out.println("\nMIME 编解码");
    String mime = Base64.getMimeEncoder().encodeToString(("subjects?abcdsubjects?abcdsubjects?abcdsubjects?abc" +
            "dsubjects?abcdsubjects?abcdsubjects?abcd").getBytes(StandardCharsets.UTF_8));
    System.out.println(mime);
    System.out.println(new String(Base64.getMimeDecoder().decode(mime), StandardCharsets.UTF_8));
}

结果

Basic 编解码
c3ViamVjdHM/YWJjZA==
subjects?abcd

URL 编解码
c3ViamVjdHM_YWJjZA==
subjects?abcd

MIME 编解码
c3ViamVjdHM/YWJjZHN1YmplY3RzP2FiY2RzdWJqZWN0cz9hYmNkc3ViamVjdHM/YWJjZHN1Ympl
Y3RzP2FiY2RzdWJqZWN0cz9hYmNkc3ViamVjdHM/YWJjZA==
subjects?abcdsubjects?abcdsubjects?abcdsubjects?abcdsubjects?abcdsubjects?abcdsubjects?abcd

封装输入输出流

编码器封装输出流,将给定字符串编码为 Base64 后写入文件输出流
解码器封装输入流,对 Base64 编码的文件流进行解码

@Test
public void wrapStreamTest() throws Exception {
    // 创建的文件是 target/test-classes/file-base64.txt
    File file = new File(this.getClass().getResource("/").getPath() + "file-base64.txt");
    try (
            // 编码器封装输出流,将给定字符串编码为 Base64 后写入文件输出流
            OutputStream os = Base64.getEncoder().wrap(new FileOutputStream(file));
    ) {
        IOUtils.write(System.getProperties().toString(), os, StandardCharsets.UTF_8);
    }

    try (
            // 解码器封装输入流,对 Base64 编码的文件流进行解码
            InputStream is = Base64.getDecoder().wrap(new FileInputStream(file))
    ) {
        IOUtils.readLines(is, StandardCharsets.UTF_8).forEach(System.out::println);
    }
}

Java 8实现BASE64编解码
http://masikkk.com/article/Java-Basic/#base64-%E7%BC%96%E7%A0%81


Linux base64命令

对文件、标准输入进行 base64 编解码,并打印到标注输出

-d, –decode 解码数据
-i, –ignore-garbag 解码时忽略非字母字符
-w, –wrap=字符数 在指定的字符数后自动换行(默认为76),0 为禁用自动换行
–version 显示版本信息并退出

如果没有指定文件,或者文件为”-“,则从标准输入读取。

数据以 RFC 3548 规定的 Base64 字母格式进行编码。 解码时,输入数据(加密流)可能包含一些非有效 Base64 字符的新行字符。可以尝试用 –ignore-garbage 选项来恢复加密流中任何非 base64 字符。

base64 从标准输入中读取数据,按Ctrl+D结束输入。将输入的内容编码为base64字符串输出。

echo "str" | base64 将字符串 str+换行 编码为base64字符串输出。
echo -n "str" | base64 将字符串str编码为base64字符串输出,最后没有换行。注意与上面的差别。

base64 file 从指定的文件file中读取数据,编码为base64字符串输出。

base64 -d 从标准输入中读取已经进行base64编码的内容,解码输出。

base64 -d -i 从标准输入中读取已经进行base64编码的内容,解码输出。加上-i参数,忽略非字母表字符,比如换行符。

echo "str" | base64 -d 将base64编码的字符串str+换行 解码输出。
echo -n "str" | base64 -d 将base64编码的字符串str解码输出,最后没有换行。注意与上面的差别。

base64 -d file 从指定的文件file中读取base64编码的内容,解码输出。


Spring提供的Base64Utils工具类

public static String encodeToString(byte[] src) 字节数组编码为 base64


commons-codec提供的Base64工具类

public static String encodeBase64String(final byte[] binaryData)


Hex十六进制转babse64

在线hex转base64

方法一 在线工具
打开这个在线工具 https://the-x.cn/zh-cn/base64
“常规base64”,填入 十六进制 值(注意开头的0x要去掉),“编码源格式” 选择 Hex 转 base64,点击“编码”,得到 base64 编码的十六进制。

java hex转base64

方法二 java 代码
写个 java 代码单测,使用 commons-lang-codec 的 Hex 工具类把十六进制字符串转为 byte 数组,再用 spring-core 的 Base64Utils 转换为 base64

@Test
public void hexoToByteArray() throws Exception {
    byte[] bytes = Hex.decodeHex("4E6AE63D".toCharArray());
    System.out.println("base64:");
    System.out.println(Base64Utils.encodeToString(bytes));
}

上一篇 护肤

下一篇 JaCoCo

阅读
评论
2.4k
阅读预计9分钟
创建日期 2020-10-28
修改日期 2021-07-07
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论