整个文章围绕前端如何进行图片处理,然后穿插介绍二进制、Blob、Blob URL、Base64、Data URL、ArrayBuffer、TypedArray、DataView 和图片压缩相关的知识点。
一、 「选择本地图片 -> 图片预览」
1.1 FileReader API
在支持 FileReader API 的浏览器中,我们也可以利用该 API 方便实现图片本地预览功能。
image.png
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>图片本地预览示例</title>
</head>
<body>
<h3>阿宝哥:图片本地预览示例</h3>
<input type="file" accept="image/*" onchange="loadFile(event)" />
<img id="previewContainer" />
<script>
const loadFile = function (event) {
const reader = new FileReader();
reader.onload = function () {
const output = document.querySelector("#previewContainer");
output.src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
};
</script>
</body>
</html>
FileReader的一些API:
https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
我们可以在 Elements 面板中看到 Data URL 的 数据:
image.png
这串奇怪的字符串被称为 Data URL,它由四个部分组成:前缀(data:)、指示数据类型的 MIME 类型、如果非文本则为可选的 base64 标记、数据本身:
data:[<mediatype>][;base64],<data>
mediatype 是个 MIME 类型的字符串参考:https://www.w3school.com.cn/media/media_mimeref.asp
在 Web 项目开发过程中,为了减少 HTTP 请求的数量,对应一些较小的图标,我们通常会考虑使用 Data URL 的形式内嵌到 HTML 或 CSS 文件中。
「但需要注意的是:如果图片较大,图片的色彩层次比较丰富,则不适合使用这种方式,因为该图片经过 base64 编码后的字符串非常大,会明显增大 HTML 页面的大小,从而影响加载速度。」
1.2 Base64
「Base64」 是一种基于 64 个可打印字符来表示二进制数据的表示方法。2^6=64,6个位标识一个字符。3个字节3*8=24。标识4个base64的字符。
「Base64 常用于在处理文本数据的场合,表示、传输、存储一些二进制数据,包括 MIME 的电子邮件及 XML 的一些复杂数据。」
在 JavaScript 中,有两个函数被分别用来处理解码和编码 base64 字符串:
- btoa():该函数能够基于二进制数据 “字符串” 创建一个 base64 编码的 ASCII 字符串。
- atob():该函数能够解码通过 base64 编码的字符串数据。
需要注意的是 base64 只是一种数据编码方式,目的是为了保障数据的安全传输。但标准的 base64 编码无需额外的信息,即可以进行解码,是完全可逆的。因此在涉及传输私密数据时,并不能直接使用 base64 编码,而是要使用专门的对称或非对称加密算法。
二、网络下载图片 -> 图片预览
除了可以从本地获取图片之外,我们也可以使用 fetch API 从网络上获取图片,然后在进行图片预览。当然对于网络上可正常访问的图片地址,我们可以直接把地址赋给 img 元素,但是有的图片加密了,因为我们需要在 Web Worker 中使用 fetch API 获取图片数据并进行解密操作。
image.png
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>获取远程图片预览示例</title>
</head>
<body>
<h3>阿宝哥:获取远程图片预览示例</h3>
<img id="previewContainer" style="width: 50%;"/>
<script>
const image = document.querySelector("#previewContainer");
fetch("https://avatars1.githubusercontent.com/u/11811888?s=400&u=2d330564df93a5291ea8f261c588670ae01f20de&v=4")
.then((response) => response.blob())
.then((blob) => {
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
});
</script>
</body>
</html>
image.png
以上的特殊字符串,我们称之为 「Object URL」,相比前面介绍的 Data URL,它简洁很多。接下来我们来认识一下 Object URL 这种协议。
2.1 Object URL
Object URL 是一种伪协议,也被称为 Blob URL。它允许 Blob 或 File 对象用作图像,下载二进制数据链接等的 URL 源。
在浏览器中,我们使用 URL.createObjectURL 方法来创建 Blob URL,该方法接收一个 Blob 对象,并为其创建一个唯一的 URL,
其形式为 blob:<origin>/<uuid>,对应的示例如下:
blob:https://example.org/40a5fb5a-d56d-4a33-b4e2-0acf6a8e5f641
浏览器内部为每个通过 URL.createObjectURL 生成的 URL 存储了一个 「URL → Blob」 映射。因此,此类 URL 较短,但可以访问 Blob。生成的 URL 仅在当前文档打开的状态下才有效。但如果你访问的 Blob URL 不再存在,则会从浏览器中收到 404 错误。
上述的 Blob URL 看似很不错,但实际上它也有副作用。虽然存储了 URL → Blob 的映射,但 Blob 本身仍驻留在内存中,浏览器无法释放它。映射在文档卸载时自动清除,因此 Blob 对象随后被释放。但是,如果应用程序寿命很长,那不会很快发生。因此,如果我们创建一个 Blob URL,即使不再需要该 Blob,它也会存在内存中。
针对这个问题,我们可以调用 URL.revokeObjectURL(url) 方法,从内部映射中删除引用,从而允许删除 Blob(如果没有其他引用),并释放内存。
既然讲到了 Blob URL,不得不提 Blob。那么什么是 Blob 呢?我们继续往下看。
2.2 Blob API
Blob(Binary Large Object)表示二进制类型的大对象。在数据库管理系统中,将二进制数据存储为一个单一个体的集合。Blob 通常是影像、声音或多媒体文件。「在 JavaScript 中 Blob 类型的对象表示不可变的类似文件对象的原始数据。」
image.pngmyBlob 对象含有两个属性:size 和 type。其中 size 属性用于表示数据的大小(以字节为单位),type 是 MIME 类型的字符串。Blob 表示的不一定是 JavaScript 原生格式的数据。比如 File 接口基于 Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。
Blob 由一个可选的字符串 type(通常是 MIME 类型)和 blobParts 组成:
image.png2.2.1 Blob 构造函数
Blob 构造函数的语法为:
var aBlob = new Blob(blobParts, options);
- blobParts:它是一个由 ArrayBuffer,ArrayBufferView,Blob,DOMString 等对象构成的数组。DOMStrings 会被编码为 UTF-8。
- options:一个可选的对象,包含以下两个属性:
2.1 type —— 默认值为 "",它代表了将会被放入到 blob 中的数组内容的 MIME 类型。
2.2 endings —— 默认值为 "transparent",用于指定包含行结束符 \n 的字符串如 何被写入。它是以下两个值中的一个:"native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持 blob 中保存的结束符不变。
let myBlobParts = ['<html><h2>Hello Semlinker</h2></html>']; // an array consisting of a single DOMString
let myBlob = new Blob(myBlobParts, {type : 'text/html', endings: "transparent"}); // the blob
console.log(myBlob.size + " bytes size");
// Output: 37 bytes size
console.log(myBlob.type + " is the type");
// Output: text/html is the type
let hello = new Uint8Array([72, 101, 108, 108, 111]); // 二进制格式的 "hello"
let blob = new Blob([hello, ' ', 'semlinker'], {type: 'text/plain'});
介绍完 Blob 构造函数,接下来我们来分别介绍 Blob 类的属性和方法:
image.png2.2.2 Blob 属性
前面我们已经知道 Blob 对象包含两个属性:
- size(只读):表示 Blob 对象中所包含数据的大小(以字节为单位)。
- type(只读):一个字符串,表明该 Blob 对象所包含数据的 MIME 类型。如果类型未知,则该值为空字符串。
2.2.3 Blob 方法
- slice([start[, end[, contentType]]]):返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。
- stream():返回一个能读取 blob 内容的 ReadableStream。
- text():返回一个 Promise 对象且包含 blob 所有内容的 UTF-8 格式的 USVString。
- arrayBuffer():返回一个 Promise 对象且包含 blob 所有内容的二进制格式的 ArrayBuffer。
「Blob 对象是不可改变的」。我们不能直接在一个 Blob 中更改数据,但是我们可以对一个 Blob 进行分割,从其中创建新的 Blob 对象,将它们混合到一个新的 Blob 中。这种行为类似于 JavaScript 字符串:我们无法更改字符串中的字符,但可以创建新的更正后的字符串。
对于 fetch API 的 Response 对象来说,该对象除了提供 blob() 方法之外,还提供了 json()、 text() 、formData() 和 arrayBuffer() 等方法,用于把响应转换为不同的数据格式。
对于前面的示例,我们把响应对象转换为 ArrayBuffer 对象,同样可以正常显示从网络下载的图像,具体的代码如下所示:
<h3>阿宝哥:获取远程图片预览示例</h3>
<img id="previewContainer" style="width: 50%;"/>
<script>
const image = document.querySelector("#previewContainer");
fetch("https://avatars3.githubusercontent.com/u/4220799")
.then((response) => response.arrayBuffer())
.then((buffer) => {
const blob = new Blob([buffer]);
const objectURL = URL.createObjectURL(blob);
image.src = objectURL;
});
</script>
网友评论