一、为什么要使用 base64

我们知道一个字节可表示的范围是 0 ~ 255(十六进制:0x00 ~ 0xFF), 其中 ASCII 值的范围为 0 ~ 127(十六进制:0x00 ~ 0x7F);而超过 ASCII 范围的 128~255(十六进制:0x80 ~ 0xFF)之间的值是不可见字符。

ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本延伸美国标准信息交换码则可以部分支持其他西欧语言,并等同于国际标准 ISO/IEC 646。

在 ASCII 码中 0 - 31和 127 是控制字符,共 33 个。以下是其中一部分控制字符:

其余 95 个,即 32 - 126 是可打印字符,包括数字、大小写字母、常用符号等。

当不可见字符在网络上传输时,比如说从 A 计算机传到 B 计算机,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。

为了解决这个问题,我们可以先对数据进行编码,比如 base64 编码,变成可见字符,也就是 ASCII 码可表示的可见字符,从而确保数据可靠传输。Base64 的内容是有 0 ~ 9,a ~ z,A ~ Z,+,/ 组成,正好 64 个字符,这些字符是在 ASCII 可表示的范围内,属于 95 个可见字符的一部分。

二、什么是 base64

Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法。由于 2⁶ = 64 ,所以每 6 个比特为一个单元,对应某个可打印字符。3 个字节有 24 个比特,对应于 4 个 base64 单元,即 3 个字节可由 4 个可打印字符来表示。相应的转换过程如下图所示:

Base64 常用于在处理文本数据的场合,表示、传输、存储一些二进制数据,包括 MIME 的电子邮件及 XML 的一些复杂数据。在 MIME 格式的电子邮件中,base64 可以用来将二进制的字节序列数据编码成 ASCII 字符序列构成的文本。使用时,在传输编码方式中指定 base64。使用的字符包括大小写拉丁字母各 26 个、数字 10 个、加号 + 和斜杠 /,共 64 个字符,等号  = 用来作为后缀用途。Base64 相应的索引表如下:

了解完上述的知识,我们以编码 Man 字符串为例,来直观的感受一下编码过程。Man 由 M、a 和 n 3 个字符组成,它们对应的 ASCII 码为 77、97 和 110。

接着我们以每 6 个比特为一个单元,进行 base64 编码操作,具体如下图所示:

由图可知,Man (3字节)编码的结果为 TWFu(4字节),很明显经过 base64 编码后体积会增加 1/3。Man 这个字符串的长度刚好是 3,我们可以用 4 个 base64 单元来表示。但如果待编码的字符串长度不是 3 的整数倍时,应该如何处理呢?

如果要编码的字节数不能被 3 整除,最后会多出 1 个或 2 个字节,那么可以使用下面的方法进行处理:先使用 0 字节值在末尾补足,使其能够被 3 整除,然后再进行 base64 的编码。

以编码字符 A 为例,其所占的字节数为 1,不能被 3 整除,需要补 2 个字节,具体如下图所示:

由上图可知,字符 A 经过 base64 编码后的结果是 QQ==,该结果后面的两个 = 代表补足的字节数。而最后个 1 个 base64 字节块有 4 位是 0 值。

接着我们来看另一个示例,假设需编码的字符串为 BC,其所占字节数为 2,不能被 3 整除,需要补 1 个字节,具体如下图所示:

由上图可知,字符串 BC 经过 base64 编码后的结果是 QkM=,该结果后面的 1 个 = 代表补足的字节数。而最后个 1 个 base64 字节块有 2 位是 0 值。

三、base64 编码的应用

3.1 显示 base64 编码的图片

在编写 HTML 网页时,对于一些简单图片,通常会选择将图片内容直接内嵌在网页中,从而减少不必要的网络请求,但是图片数据是二进制数据,该怎么嵌入呢?绝大多数现代浏览器都支持一种名为 Data URLs 的特性,允许使用 base64 对图片或其他文件的二进制数据进行编码,将其作为文本字符串嵌入网页中。

Data URLs 由四个部分组成:前缀(data:)、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身:

data:[<mediatype>][;base64],<data>

mediatype 是个 MIME 类型的字符串,例如 "image/jpeg" 表示 JPEG 图像文件。如果被省略,则默认值为 text/plain;charset=US-ASCII。如果数据是文本类型,你可以直接将文本嵌入(根据文档类型,使用合适的实体字符或转义字符)。如果是二进制数据,你可以将数据进行 base64 编码之后再进行嵌入。比如嵌入一张图片:

<img alt="logo" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...">

MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

常见的 MIME 类型有:超文本标记语言文本 .html text/html、PNG图像 .png image/png、普通文本 .txt text/plain 等。

但需要注意的是:如果图片较大,图片的色彩层次比较丰富,则不适合使用这种方式,因为该图片经过 base64 编码后的字符串非常大,会明显增大 HTML 页面的大小,从而影响加载速度。 除此之外,利用 HTML FileReader API,我们也可以方便的实现图片本地预览功能,具体代码如下:

<input type="file" accept="image/*" onchange="loadFile(event)">
<img id="output"/>
<script>const loadFile = function(event) {const reader = new FileReader();reader.onload = function(){const output = document.querySelector('#output');output.src = reader.result;};reader.readAsDataURL(event.target.files[0]);};
</script>

在完成本地图片预览之后,可以直接把图片对应的 Data URLs 数据提交到服务器。针对这种情形,服务端需要做一些相关处理,才能正常保存上传的图片,这里以 Express 为例,具体处理代码如下:

const app = require('express')();app.post('/upload', function(req, res){let imgData = req.body.imgData; // 获取POST请求中的base64图片数据let base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");let dataBuffer = Buffer.from(base64Data, 'base64');fs.writeFile("image.png", dataBuffer, function(err) {if(err){res.send(err);}else{res.send("图片上传成功!");}});
});

3.2 浏览器端图片压缩

在一些场合中,我们希望在上传本地图片时,先对图片进行一定的压缩,然后再提交到服务器,从而减少传输的数据量。在前端要实现图片压缩,我们可以利用 Canvas 对象提供的 toDataURL() 方法,该方法接收 typeencoderOptions 两个可选参数。

其中 type 表示图片格式,默认为 image/png。而 encoderOptions 用于表示图片的质量,在指定图片格式为 image/jpegimage/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92,其他参数会被忽略。

下面我们来看一下具体如何实现图片压缩:

// compress.js
const MAX_WIDTH = 800; // 图片最大宽度function compress(base64, quality, mimeType) {let canvas = document.createElement("canvas");let img = document.createElement("img");img.crossOrigin = "anonymous";return new Promise((resolve, reject) => {img.src = base64;img.onload = () => {let targetWidth, targetHeight;if (img.width > MAX_WIDTH) {targetWidth = MAX_WIDTH;targetHeight = (img.height * MAX_WIDTH) / img.width;} else {targetWidth = img.width;targetHeight = img.height;}canvas.width = targetWidth;canvas.height = targetHeight;let ctx = canvas.getContext("2d");ctx.clearRect(0, 0, targetWidth, targetHeight);ctx.drawImage(img, 0, 0, canvas.width, canvas.height);let imageData = canvas.toDataURL(mimeType, quality / 100);resolve(imageData);};});
}

对于返回的 Data URL 格式的图片数据,为了进一步减少传输的数据量,我们可以把它转换为 Blob 对象:

function dataUrlToBlob(base64, mimeType) {let bytes = window.atob(base64.split(",")[1]);let ab = new ArrayBuffer(bytes.length);let ia = new Uint8Array(ab);for (let i = 0; i < bytes.length; i++) {ia[i] = bytes.charCodeAt(i);}return new Blob([ab], { type: mimeType });
}

在转换完成后,我们就可以压缩后的图片对应的 Blob 对象封装在 FormData 对象中,然后再通过 AJAX 提交到服务器上:

function uploadFile(url, blob) {let formData = new FormData();let request = new XMLHttpRequest();formData.append("image", blob);request.open("POST", url, true);request.send(formData);
}

其实 Canvas 对象除了提供 toDataURL() 方法之外,它还提供了一个 toBlob() 方法,该方法的语法如下:

canvas.toBlob(callback, mimeType, qualityArgument)

toDataURL() 方法相比,toBlob() 方法是异步的,因此多了个 callback 参数,这个 callback 回调方法默认的第一个参数就是转换好的 blob 文件信息。

介绍完上述的内容,我们来看一下本地图片压缩完整的示例:

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>本地图片压缩</title></head><body><input type="file" accept="image/*" onchange="loadFile(event)" /><script src="./compress.js"></script><script>const loadFile = function (event) {const reader = new FileReader();reader.onload = async function () {let compressedDataURL = await compress(reader.result,90,"image/jpeg");let compressedImageBlob = dataUrlToBlob(compressedDataURL);uploadFile("https://httpbin.org/post", compressedImageBlob);};reader.readAsDataURL(event.target.files[0]);};</script></body>
</html>

四、如何进行 base64 编码和解码

4.1 使用 btoa 与 atob 函数

在 JavaScript 中,有两个函数被分别用来处理解码和编码 base64 字符串:

  • btoa():从字符串创建一个 base64 编码的 ASCII 字符串,其中字符串中的每个字符都被视为一个二进制数据字节。

  • atob():该函数能够解码通过 base64 编码的字符串数据。

btoa 使用示例
const name = 'Semlinker';
const encodedName = btoa(name);
console.log(encodedName); // U2VtbGlua2Vy
atob 使用示例
const encodedName = 'U2VtbGlua2Vy';
const name = atob(encodedName);
console.log(name); // Semlinker

介绍完 btoaatob 这两个函数,我们再来看一下它们的兼容性:

(图片来源 —— https://caniuse.com/?search=atob)

由上图可知,除了 IE6-9 和 Opera 10.1 这些版本的浏览器之外,主流的浏览器都支持 btoaatob 这两个函数。

4.2 使用第三方库

对于不支持 btoaatob 这两个函数的浏览器来说,我们可以使用第三方库,比如 js-base64 这个库,来实现 base64 的编码和解码。

具体的使用示例如下:

<!DOCTYPE html>
<html lang="zh"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Base64 编码与解码示例</title><script src="https://cdn.jsdelivr.net/npm/js-base64@3.6.0/base64.min.js"></script></head><body><h3>Base64 编码与解码示例</h3><script>let name = Base64.encode("阿宝哥");console.log(name);console.log(Base64.decode(name));</script></body>
</html>

在前端进行二进制处理的场景中,你可能会遇到 Data URL 转换成 Blob/File 对象的情形,接下来阿宝哥将汇总一下常用的转换函数。

五、常用转换函数

5.1 Data URL 转 Blob 对象

function dataUrlToBlob(dataurl, mimeType) {let bytes = window.atob(dataurl.split(",")[1]);let ab = new ArrayBuffer(bytes.length);let ia = new Uint8Array(ab);for (let i = 0; i < bytes.length; i++) {ia[i] = bytes.charCodeAt(i);}return new Blob([ab], { type: mimeType });
}// 使用示例
let blob = dataUrlToBlob('data:text/plain;base64,aGVsbG8gd29ybGQ=','hello.txt');
console.log(blob);

5.2 Data URL 转 File 对象

function dataUrlToFile(dataurl, filename) {let arr = dataurl.split(","),mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);while (n--) {u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], filename, { type: mime });
}// 使用示例
let file = dataUrlToFile('data:text/plain;base64,aGVsbG8gd29ybGQ=','hello.txt');
console.log(file);

5.3 URL 转 File 对象

function urlToFile(url, filename, mimeType) {return fetch(url).then((res) => {return res.arrayBuffer();}).then((buffer) =>{return new File([buffer], filename, { type: mimeType });});
}// 使用示例
urlToFile('data:text/plain;base64,aGVsbG8gd29ybGQ=', 'hello.txt','text/plain').then(function(file){ console.log(file);});

六、总结

Base64 是一种数据编码方式,目的是为了保障数据的安全传输。但标准的 base64 编码无需额外的信息,即可以进行解码,是完全可逆的。因此在涉及传输私密数据时,并不能直接使用 base64 编码,而是要使用专门的对称或非对称加密算法。如果你对加密感兴趣的话,可以阅读 玩转混合加密 这篇文章。

七、参考资源

  • 维基百科 - Base64

  • Base64 编码原理与应用

  • 知乎 - 为什么要使用base64编码,有哪些情景需求?

【拓展】868- 一文读懂base64相关推荐

  1. 【总结】1026- 一文读懂 base64

    一.为什么要使用 base64 我们知道一个字节可表示的范围是 0 - 255(十六进制:0x00 - 0xFF), 其中 ASCII 值的范围为 0 - 127(十六进制:0x00 - 0x7F): ...

  2. 一文读懂Faster RCNN

    来源:信息网络工程研究中心本文约7500字,建议阅读10+分钟 本文从四个切入点为你介绍Faster R-CNN网络. 经过R-CNN和Fast RCNN的积淀,Ross B. Girshick在20 ...

  3. 语言相关系数显著性_相关性分析在SPSS中的具体操作,一文读懂相关系数的含义及使用——【杏花开生物医药统计】...

    相关性分析介绍 生物和医学统计中,相关分析属于流程前端的探索性分析,研究变量间关系及性质,其结果在为下一步采取何种方法做出指引,为数据挖掘之前的基础工作. 相关系数的选择 相关分析之前,需要先确认变量 ...

  4. 技术向:一文读懂卷积神经网络

     技术向:一文读懂卷积神经网络 技术网络 36大数据(张雨石) · 2015-03-06 05:47 自今年七月份以来,一直在实验室负责卷积神经网络(Convolutional Neural Ne ...

  5. 一文读懂程序化易法易化资频易计利

    转 一文读懂程序化交易.算法交易.量化投资.高频交易. 统计套利 在央行发布的<中国金融稳定报告(2016)>中,对于高频交易的解释为程序化交易的频率超过一定程度,就成为高频交易.而对程序 ...

  6. 一文读懂程序化交易算法交易量化投资高频交易统计利

    转 一文读懂程序化交易.算法交易.量化投资.高频交易. 统计套利 在央行发布的<中国金融稳定报告(2016)>中,对于高频交易的解释为程序化交易的频率超过一定程度,就成为高频交易.而对程序 ...

  7. 一文读懂PQuant与QQuant量化易金工

    转 一文读懂P Quant与 Q Quant ,量化交易与金融工程 原标题:P Quant 和 Q Quant 到底哪个是未来?  来源:李老师与何老师的CFA学习课堂  作者:何璇 P-Quant ...

  8. 一文读懂智能网联封闭测试场的现状和挑战

    文章版权所有,未经授权请勿转载或使用 智能网联封闭测试场应具备全覆盖.低延时的路侧通信设备,支持LTE-V2X.5G.Wi-Fi等协议.可进行三个级别测试:L1和L2级自动驾驶的ADAS系统测试:L3 ...

  9. 从实验室走向大众,一文读懂Nanopore测序技术的发展及应用

    关键词/Nanopore测序技术    文/基因慧 随着基因测序技术不断突破,二代测序的发展也将基因检测成本大幅降低.理想的测序方法,是对原始DNA模板进行直接.准确的测序,消除PCR扩增带来的偏差, ...

最新文章

  1. 用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解
  2. RegistryCallback routine(CmRegisterCallback 注册表操作监控介绍)
  3. ASP.NET中新建MVC项目并连接SqlServer数据库实现增删改查
  4. 关于恐怖小说“分级”及其他:答《北京娱乐信报》记者张守刚问
  5. Docker之Dockerfile详解
  6. Excel显示完整的年月日乱码解决方法
  7. 内核参数 linux dd,Linux dd 命令详解(测磁盘的吞吐量)
  8. 【BZOJ4378】[POI2015]Logistyka 树状数组
  9. C++dll导出类的方式__declspec(dllexport)
  10. [备忘]windows下安装PHP环境php.ini-recommended 跟php.ini-dist 的差别之处
  11. RAID 0/1/5/6/00/(10/01)/50/60磁盘阵列逻辑容量计算公式
  12. 读Java 804 - Quick refresher
  13. WhatsApp的下载与更新
  14. 模型量化论文阅读#3----Up or Down? Adaptive Rounding for Post-Training Quantization
  15. ubuntu 16.04 和 18.04 替换apt源为阿里源
  16. 操作系统真象还原第1.5章 NASM汇编学习
  17. eclipse插件安装失败的列表如何清除-一个困扰很久的问题
  18. 谷歌修改useragent,chrome模拟微信、QQ内置浏览器
  19. 免费服务器硬件资源管理工具,wgcloud 国人免费版服务器资源监控工具
  20. 接触式IC卡 - STM32(Smart Card)

热门文章

  1. 大数据如何采集数据?大数据的数据从何而来?
  2. ofd 格式 Linux解析,OFD板式文档-标准格式内容解读
  3. linux移动数据,基于嵌入式Linux移动数据同步研究与实现
  4. 07-如何选购台式电脑硬盘?小白装机选购固态硬盘和机械硬盘知识指南
  5. Python基础知识之pandas加载本地txt格式或csv格式的数据
  6. Word,Excel大小写批量转换
  7. Unity巡逻兵游戏-与怪兽战斗!
  8. The Unarchiver 4 for Mac(解压缩软件)
  9. 高校聚合支付平台设计解决方案
  10. win7电脑怎么录屏?超级简单的2种电脑录屏方法