简要了解

Base 64是一种使64个字符来表示任意二进制数据的方法。Base 64是一种非常常用的二进制编解码方案。

有些说法是说,使用Base 64作为加密解密的功能,比如一个Web系统中,密码字段存入数据库的时候使用Base64.encode一下,其实Base 64的编码、解码方法都是简单公开的,用以加密、解密实在谈不上啥保障性。

在Java 8中,整合了Base 64。Java 8 为开发者提供了 java.util.Base64 的工具类,Java 8现在有内置编码器和解码器的Base 64编码。在Java 8中有三种类型的Base 64编码。

第一种,简单Base 64

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

输出映射设置字符在A-ZA-Z0-9+/。编码器不添加任何换行输出和解码器拒绝在A-Za-z0-9+/以外的任何字符。

第二种,URL编码

输出映射设置字符在A-Za-z0-9-_。输出URL和文件名安全。由于标准的Basic编码可能会出现+/ ,在URL中就不能直接作为参数,所以又有一种“url safe” 的Base 64编码,其实就是吧字符+/分别变成-_

第三种,MIME编码

输出映射到MIME友好的格式。输出表示在每次不超过76个字符行和使用'\r'后跟一个换行符'\n'回车作为行分隔符。无行隔板的存在是为了使编码的结束输出。MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用对应的应用程序来打开的方式类型。

它是一种互联网标准,扩展了电子邮件标准,使其可以支持:非ASCII字符文本;非文本格式附件(二进制、声音、图像等);由多部分组成的消息体;包含非ASCII字符的头信息等。

举个栗子:右键中有一个Word附件(二进制文件),点击预览,浏览器会直接打开Office。可以理解为MIME设定了这种对应关系。

MIME编码器会使用基本的字母数字产生Base 64输出,而且对MIME格式友好:每一行输出不超过76个字符,而且每行以“\r\n”符结束。

实用API与代码示例

API

Base 64类

1Base64.getEncoder()返回一个 Base64.Encoder ,编码使用基本型 base64 编码方案
2Base64.getDecoder()返回一个 Base64.Decoder ,解码使用基本型 base64 编码方案
3Base64. getUrlEncoder()返回一个 Base64.Encoder ,编码使用 URL 和文件名安全型 base64 编码方案
4Base64.getUrlDecoder()返回一个 Base64.Decoder ,解码使用 URL 和文件名安全型 base64 编码方案。
5Base64.getMimeEncoder()返回一个 Base64.Encoder ,编码使用 MIME 型 base64 编码方案。
6Base64.getMimeEncoder(int lineLength, byte[] lineSeparator)返回一个 Base64.Encoder ,编码使用 MIME 型 base64 编码方案,可以通过参数指定每行的长度及行的分隔符。
7Base64.getMimeDecoder()返回一个 Base64.Decoder ,解码使用 MIME 型 base64 编码方案。

内嵌类Encoder

1byte[] encode(byte[] src)编码,返回一个byte数组
2int encode(byte[] src, byte[] dst)编码,写进新的byte数组
3String encodeToString(byte[] src)编码,返回一个字符串
4ByteBuffer encode(ByteBuffer buffer)编码
5OutputStream wrap(OutputStream os)编码
6Encoder withoutPadding()编码

内嵌类Decoder

1byte[] decode(byte[] src)解码,返回一个byte数组,入参为byte数组
2byte[] decode(String src)解码,返回一个byte数组,入参为字符串
3int decode(byte[] src, byte[] dst)解码
4ByteBuffer decode(ByteBuffer buffer)解码
5InputStream wrap(InputStream is)解码

代码示例

package com.kjgym.common.java8;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

/**
 * @author kjgym
 * @description Demo for Java8's Base64
 * @date 2019/9/17 11:25
 */
public class Java8Base642 {

    public static void main(String[] args) {
        final String demoStr = "kjgym";
        final byte[] strByte = demoStr.getBytes(StandardCharsets.UTF_8);

        // 基本型编码方案
        String s = Base64.getEncoder().encodeToString(strByte);
        System.out.println("基本编码: " + s);
        String ss = new String(Base64.getDecoder().decode(s));
        System.out.println("解码: " + ss);

        // 使用 URL 和文件名安全型 base64 编码方案。
        String s1 = Base64.getEncoder().encodeToString(strByte);
        System.out.println("URL编码: " + s1);
        String ss1 = new String(Base64.getUrlDecoder().decode(s));
        System.out.println("解码: " + ss1);

        // 使用 MIME 型 base64 编码方案
        String s2 = Base64.getMimeEncoder().encodeToString(strByte);
        System.out.println("MIME编码: " + s2);
        String ss2 = new String(Base64.getMimeDecoder().decode(s));
        System.out.println("解码: " + ss2);

    }
}

结果:

阅读源码

  1. 先来总体看一下Base 类,该类主要包含两个内嵌类EncoderDecoder以及七个构造内嵌类的方法:
  1. 再看两个内嵌类。比如Encoder(部分源代码):

    public static class Encoder {
    
            private final byte[] newline;
            private final int linemax;
            private final boolean isURL;
            private final boolean doPadding;
    
            private Encoder(boolean isURL, byte[] newline, int linemax, boolean doPadding {
                this.isURL = isURL;
                this.newline = newline;
                this.linemax = linemax;
                this.doPadding = doPadding;
            }
    
            static final Encoder RFC4648 = new Encoder(false, null, -1, true);
            static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
            static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
    
            public byte[] encode(byte[] src) {}
    
            public int encode(byte[] src, byte[] dst) {}
    
            @SuppressWarnings("deprecation")
            public String encodeToString(byte[] src) {}
    
            public ByteBuffer encode(ByteBuffer buffer) {}
    
            public OutputStream wrap(OutputStream os) {}
    
            public Encoder withoutPadding() {}
    
            private int encode0(byte[] src, int off, int end, byte[] dst) {}
        }

    构造器私有化,对外提供单个静态对象,然后在Base 64类中,使用getEncoder的方式爱调用,使用了单例模式。

    static final Encoder RFC4648 = new Encoder(false, null, -1, true);
    static final Encoder RFC4648_URLSAFE = new Encoder(true, null, -1, true);
    static final Encoder RFC2045 = new Encoder(false, CRLF, MIMELINEMAX, true);
  2. Decoder与上文Encoder相似,这里不再赘述。有兴趣自己Ctrl + 单击去看源码。

拓展

  1. StandardCharsets类。在java.nio.charset包下的一个类,在字符串和byte数组相互转换需要制定编码的时候使用。该类有六种编码格式:

    /*
     * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     */
    package java.nio.charset;
    
    /**
     * Constant definitions for the standard {@link Charset Charsets}. These
     * charsets are guaranteed to be available on every implementation of the Java
     * platform.
     *
     * @see <a href="Charset#standard">Standard Charsets</a>
     * @since 1.7
     */
    public final class StandardCharsets {
    
        private StandardCharsets() {
            throw new AssertionError("No java.nio.charset.StandardCharsets instances for you!");
        }
        /**
         * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the
         * Unicode character set
         */
        public static final Charset US_ASCII = Charset.forName("US-ASCII");
        /**
         * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
         */
        public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
        /**
         * Eight-bit UCS Transformation Format
         */
        public static final Charset UTF_8 = Charset.forName("UTF-8");
        /**
         * Sixteen-bit UCS Transformation Format, big-endian byte order
         */
        public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
        /**
         * Sixteen-bit UCS Transformation Format, little-endian byte order
         */
        public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
        /**
         * Sixteen-bit UCS Transformation Format, byte order identified by an
         * optional byte-order mark
         */
        public static final Charset UTF_16 = Charset.forName("UTF-16");
    }
    
02-13 19:47