Base64 编码与 JSON 交换

JSON 是常用的数据交换格式。但是由于 JSON 本身并不支持文件图片等形式的附加数据,想要直接借助 JSON 来实现文件的传输非常困难。

一般的做法是,首先需要使用 Base64 编码将这些数据从二进制流编码成为字符形式,再封装成 JSON 格式进行传输。

本文首先讨论 Base64 编码大致原理,然后引入 MIME 类型的简单介绍,最后通过 Web API 来实现文件的自动编码实现文件图片等内容的 JSON 传输。

Base64 编码原理

我们知道,6 bits 编码 000000 —— 111111 可以表示 2的6次方=64 个符号。反之,64个符号也可以表示 6 bits 的所有编码 000000 —— 111111。

Base64 索引表

在 Base64 编码中,每个字符用来表示 6 bits 的数据。因此 Base64 可以直接用来编码 6 及其倍数的 bit 长度的数据。

若原始数据 bit 长度不满足此倍数要求,就要对数据进行填充,并用 ‘=’ 进行标记。在解码的时候,丢弃 ‘=’ 标记的数据。

numBytes 字节长度的数据经过 Base64 编码之后,编码长度如下代码所示。

function base64Inflation (numBytes)
    minimumBase64Bytes = roundDown(numBytes / 3 * 4)    
    modulus = numberOfBytes % 3   // Assuming % is the modulo operator
    if modulus == 0 
        return minimumBase64Bytes  // Exact fit! No padding required.
    else
        return minimumBase64Bytes + 4 // Doesn't quite fit. We need to pad.

MIME 附加数据格式标准

MIME 是HTTP协议中所有附加数据格式的标准,它规定了所有附加数据的编码方式。

Base64 就是 MIME 指定的一种二进制到文本的编码方式 。

由于 MIME 限制了每行支持的最大字符数,因此最终编码中增加了额外用来控制换行的字符。

这就导致在 MIME 标准下, 最终 Base64 编码文件的大小往往是原始文件的1.37 倍左右。

JSON 传输

通过编码的二进制数据直接可以放在 JSON 中。

Web API 可以直接将读取到的图片或者文件编码成 Base64 格式的数据,并以 Data URL 的形式返回。

  onFileChange(event) {
    let reader = new FileReader();
    if(event.target.files && event.target.files.length > 0) {
      let file = event.target.files[0];
      reader.readAsDataURL(file);
      reader.onload = () => {
        this.form.get('avatar').setValue({
          filename: file.name,
          filetype: file.type,
          value: reader.result.split(',')[1]
        })
      };
    }
  }

但是需要注意的是,reader.result 的内容是一个Data URL,不能直接用于Base64 解码。假如需要进行解码,要去掉前缀描述部分。

// <del>data:text/plain;base64,</del>SGVsbG8sIFdvcmxkIQ%3D%3D
value: reader.result.split(',')[1]

通过 Data URL 获取编码之后,就可以将其放在 JSON 字串中上传给服务器。

{
  name: "John",
  avatar: {
    filename: "10x10png",
    filetype: "image/png",
    value: "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKBWlpa="
  }
}

优点与缺点

通过 JSON 传输 Base64 编码后的文件要分两点来说。

一方面,若客户端像服务器端以 JSON 形式上传资源,那么资源编码的时候需要占用客户端一些的一些时间,对于主要是移动设备的平台来说要慎重选择。

另一方面,若客户端以 JSON 形式下载资源 ,那么就需要权衡文件下载并转码的时间与 HTTP 请求次数之间的关系。

<div *ngFor="let image of images;">
   <div *ngIf="images.length > 0" >
     <img class="media" [src]="image">
   </div>
</div>

考虑这个代码片,假设 images 是异步方式请求到的 JSON 对象。

假如 images 代表的是一串 URL,那么在 HTML 渲染的时候,还需要发出多次 HTTP 从服务器请求相应的图片资源(可以并发)。

假如 images 代表的是一串包含 Base64 编码的 Data URL,那么在HTML渲染的时候,就无须再次发出 HTTP 请求,可以直接渲染。

但是这样带来的问题是,假如图片过大,通过 JSON 方式一次性传输的数据就会很大,响应时间就比较长,造成图片迟迟不能刷新。

有人通过实验数据指出,对于 Icon 类的小图片可以直接在 CSS 中以 Base64 的编码进行嵌入。而对于大图片,还是以 HTTP 请求的方式来实现,来提升用户体验。

参考文献

Wiki-Base64

Base64 encoded data maximum size

MIME types

Uploading files in Angular (2/4) to a REST api

Base64 encoded data maximum size

When to base64 encode images (and when not to)

发表评论

电子邮件地址不会被公开。 必填项已用*标注