说说二进制流
我们通常将图片/音频等文件用二进制流来表示.
计算机的存储在物理上是都二进制的,所以文本文件与二进制文件的区别并不是物理上的,而是逻辑上的。
可能有人要问了,为什么要用二进制来表示图片呢?我们来看看二进制的优势:
- 二进制文件比较节约空间,在储存字符型数据时与普通存储并没有差别。但是在储存数字,特别是实型数字时,二进制更节省空间;
- 内存中参加计算的数据都是用二进制无格式储存起来的,因此,使用二进制储存到文件就更快捷。如果储存为文本文件,则需要一个转换的过程。在数据量很大的时候,两者就会有明显的速度差别了;
- 涉及到一些比较精确的数据,使用二进制储存不会造成有效位的丢失;
看到这里,相信大家已经对二进制流有了一定的认识。既然提到了存储,补充说明一下图片在后端存储方式:
- 其一:可以将图片以独立文件的形式存储在服务器的指定文件夹中,再将路径存入数据库字段中;
- 其二:将图片转换成二进制流,直接存储到数据库的
Image
类型字段中;
base64 编码
我们有时候会在网页中看到这样的代码:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAb4AAABYCAYAAACK55LCAAAgAElEQVR4Xuy9aXNc2ZEleN4e+wIgsO8ACXAnk8mUUqoqldQ9Yz3W/an/wsxvnA/TM2NWizKVmczkTgIg9h0IxL6+fez4DbCo7JQqpZLZfGBARjEJBOK95/de9+PHj3toO//7f40x/BpaYGiBoQWGFhha4BOxgDYMfJ/ISg8fc2iBoQWGFhhaQCwwDHzDjTC0wNACQwsMLfBJWWAY
...
这就是 base64 格式的数据,代表着一张图片的数据,专业术语叫:Data URI scheme。
Base64是一种基于64个可打印字符
来表示二进制数据的表示方法。
表示方式:
由于,所以每6个 二进制位(bit) 为一个单元,对应某个可打印字符。3个 字节(byte)有24个bit,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。
编码字符的情况下,由于 Base64 仅可对 ASCII 字符进行编码,也就是单字节字符(可以理解为英文和数字,还有一些简单符号)。若编码中文字符串,则需先转换为 ASCII 字符。
在Base64中的可打印字符包括 字母A-Z
、a-z
、数字0-9
,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。
转换方式:
-
编码
const base64 = window.btoa('abc'); // YWJj
-
解码
const initString = window.atob(base64); // abc
但是 btoa、atob 仅支持对 ASCII 字符编码,也就是单字节字符,而我们平时的中文都是 3-4 字节的字符。
因此可以先将中文字符转为 utf-8 的编码,将 utf-8 的编码当做字符,这样就可以对多个单字节字符进行编码。往下看。
encodeURIComponent & decodeURIComponent
chrome 上输入网址 http://zh.wikipedia.org/wiki/春节
,然后复制下来,你会发现变成了 https://zh.wikipedia.org/wiki/%E6%98%A5%E8%8A%82
。
我们知道,"春"和"节"的 utf-8 编码分别是"E6 98 A5"和"E8 8A 82",因此,"%E6%98%A5%E8%8A%82"就是按照顺序,在每个字节前加上 %
而得到的。
这也就是 encodeURL 的功能:将非 ACSII 码的字符进行 utf-8 编码。 encodeURLComponent 与它不同的地方在于,对整个 url 进行编码(http%3A%2F%2Fzh.wikipedia.org%2Fwiki%2F%E6%98%A5%E8%8A%82
)。
所以接上文,我们可以对 url 进行编码后,将这个 utf-8 编码作为 ACSII 字符,进行 base64。
比如我们在转换中文的时候:
-
编码(多字节)
const base64 = window.btoa(encodeURIComponent('春节')); // JUU2JTk4JUE1JUU4JThBJTgy
-
解码(多字节)
const initString = decodeURIComponent(window.atob(base64)); // 春节
Blob 对象(二进制容器)
Blob (binary large object),二进制大对象,是一个可以存储二进制文件的容器。
构建方式:
const blob = new Blob(['something...'])
这是我转换的一个 Blob 对象:
Blob {
name: "图片示例:jartto.png",
preview: "blob:file:///f3823a2a-2908-44cb-81e2-c19d98abc5d1",
size: 47396,
type: "image/png",
}
请注意,这里的 preview
属性是可以拿出来做图片预览的。
base64 与 Blob 互转
在日常开发中, 最常见的便是将blob
和base64
之间相互转换.
// blob to base64
function blobTobase64(blob) {
const fileReader = new FileReader()
let base64 = ''
fileReader.onload = () => {
base64 = fileReader.result // 读取base64
}
fileReader.readAsDataURL(blob) // 读取blob
}
// base64 to blob
function dataURItoBlob(dataURI) {
var mimeString = dataURI
.split(',')[0]
.split(':')[1]
.split(';')[0] // mime类型
var byteString = atob(dataURI.split(',')[1]) //base64 解码
var arrayBuffer = new ArrayBuffer(byteString.length) //创建ArrayBuffer
var intArray = new Uint8Array(arrayBuffer) //创建视图
for (var i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i)
}
return new Blob([intArray], { type: mimeString }) // 转成 blob
}
URL 转 base64
好了,更有意思的事情来了,我提高了要求,只给你一个图片的 URL(本地或者在线地址),是否能将图片编码成流?答案是肯定的,这里我们得请出 canvas 了:
思路很简单,根据图片对象的数据去画 canvas,再利用它来等价表示图片流。
getDataUri(url) {
return new Promise((resolve, reject) => {
/* eslint-disable */
let image = new Image();
image.onload = function() {
let canvas = document.createElement('canvas');
canvas.width = this.naturalWidth;
canvas.height = this.naturalHeight;
canvas.getContext('2d').drawImage(this, 0, 0);
// Data URI
resolve(canvas.toDataURL('image/png'));
};
image.src = url;
// console.log(image.src);
image.onerror = () => {
reject(new Error('图片流异常'));
};
});
}
你可以像这样调用:
let dataUri = await this.getDataUri(`image/test/jartto.png`);
FileReader 是什么?
从上节了解到Blob对象只是二进制数据的容器,本身并不能操作二进制,故本节将对其操作对象FileReader进行介绍。
读取的 File 对象可以是什么呢?
-
来自用户在一个
<input>
元素上选择文件后返回的FileList
对象 -
来自拖放操作生成的
DataTransfer
对象 -
来自在一个
HTMLCanvasElement
上执行mozGetAsFile()
方法后返回结果 -
... ...
官话说了一大堆,其实你只需要知道「它是用来读取文件的」就够了。
参数说明如下:
- FileReader.error(只读):一个 DOMException,表示在读取文件时发生的错误 ;
- FileReader.readyState(只读):表示 FileReader 状态的数字。取值如下:
- EMPTY 0 还没有加载任何数据.
- LOADING 1 数据正在被加载.
- DONE 2 已完成全部的读取请求.
- FileReader.result(只读):文件的内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用哪个方法来启动读取操作。
还有几个事件处理函数:
- FileReader.onabort:处理 abort 事件。该事件在读取操作被中断时触发。
- FileReader.onerror:处理 error 事件。该事件在读取操作发生错误时触发。
- FileReader.onload:处理 load 事件。该事件在读取操作完成时触发。
- FileReader.onloadstart:处理 loadstart 事件。该事件在读取操作开始时触发。
- FileReader.onloadend:处理 loadend 事件。该事件在读取操作结束时(要么成功,要么失败)触发。
- FileReader.onprogress:处理 progress 事件。该事件在读取 Blob 时触发。
可以用的方法如下:
- FileReader.abort():中止读取操作。在返回时,readyState 属性为 DONE。
- FileReader.readAsArrayBuffer():开始读取指定的 Blob 中的内容。一旦完成,result 属性将包含一个ArrayBuffer 来表示文件数据;
- FileReader.readAsBinaryString()(
即将弃用
):开始读取指定的 Blob 中的内容。一旦完成,result 属性中将包含所读取文件的原始二进制数据。 - FileReader.readAsDataURL():开始读取指定的 Blob 中的内容。一旦完成,result 属性中将包含一个 data: URL 格式的字符串以表示所读取文件的内容。(base64编码)
- FileReader.readAsText():开始读取指定的 Blob 中的内容。一旦完成,result 属性中将包含一个字符串以表示所读取的文件内容。
上面的文档描述的很清楚了,但是估计你也没逐条细看,没关系,我们还有示例?
具体的应用场景可能如下:
const blob = new Blob(['你']);
const reader = new FileReader();
reader.readAsArrayBuffer(blob);
reader.onload = (e) => {
// ArrayBuffer(3) {}
let result = e.target.result;
// 这里使用Unit8解读ArrayBuffer
const v1 = new Uint8Array(result);
// "你" 这个字符默认转为utf-8编码,占据三个字节;这里为10进制展示,只是为了直观
console.log(v1) // Unit8Array(3) [228, 189, 160]
}
---------------------------------------------------------------
// 同样我们可以将二进制解读出文本(假设我们拿到了 v1 Unit8二进制数组对象)
const blob = new Blob([v1]);
let reader = new FileReader();
reader.readAsText(blob);
reader.onload = (e) => {
console.log(e.target.result) // 你
}
用法很简单,你唯一需要注意的是一定要在 onload
之后再做操作!
常见问题与补充说明
-
Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'.
出现如上问题,请检查你的 Blob 对象格式是否正确,是否缺少必要项。
-
很多第三方应用都需要去除 base64 头信息:
base64.replace(/^data:image\/(jpeg|png|gif);base64,/,'')
-
FileReader 读取文件后,记得放在
onload
函数内,切记。 -
在使用
canvas.getContext('2d').drawImage(this, 0, 0);
过程中,一定要注意这里的this
指向,否则可能发生异常。 -
base64 转 Blob,请记得:
Object.assign(blob,{ // jartto: 这里一定要处理一下 preview: URL.createObjectURL(blob), name: `图片示例:${index}.png` });
参考
- MDN FileReader
- ASCII,Unicode,UTF-8和Base64
- Convert Image to Data URI
- html base64 img 图片显示
- 解决 Javascript 中 atob 方法解码中文字符乱码问题
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!