JavaScript实战笔记(五) 预览本地图片
一般情况下,实现本地图片预览有两种方法,一种是 DataURL,一种是 BlobURL
所以在开始介绍怎么展示本地图片之前,我们花一点时间了解一下什么是 DataURL 和 BlobURL
1、DataURL
(1)介绍
DataURL 就是以 data:
开头的 URL,它将数据编码成特定的格式,并允许开发者在文档中嵌入
(2)格式
data:[<mediatype>][;base64],<data>
-
data:
:前缀部分,DataURL 都必须以data:
开头 -
[<mediatype>]
:表明数据的 MIME 类型,可选项,默认为text/plain;charset=US-ASCII
MIME 的标准格式是
type/subtype
,其中,type
表明数据的分类,subtype
表明具体的类型常见的
type
和subtype
如下:-
若
type
为text
,表明数据是普通文本,例如text/plain
、text/html
在DataURL 中,如果数据是普通文本,还可以在后面用
charset
指定编码,例如charset=UTF-8
-
若
type
为image
,表明数据是图像类型,例如image/jpeg
、image/png
-
若
type
为audio
,表明数据是音频类型,例如audio/mpeg
、audio/wav
-
若
type
为video
,表明数据是视频类型,例如video/webm
、video/ogg
-
若
type
为application
,表明数据是二进制类型,例如application/octet-stream
-
-
[;base64]
:可选项,添加后表明后面的数据<data>
是经过 Base64 编码的如果数据是简单文本,那么可以直接嵌入,不需要经过 Base64 编码,因此可以省略
如果数据是其它类型,那么必须加上
;base64
,并在后面嵌入以 Base64 编码的数据 -
<data>
:实际的数据,可以是普通文本,可以是经过 Base64 编码的数据如果是经过 Base64 编码的数据,那么前面必须要有
;base64
(3)例子
- 普通文本
// 不指定文本编码,显示乱码:浣犲ソ
data:text/plain,你好
// 指定文本编码为 UTF-8,显示正确:你好
data:text/plain;charset=UTF-8,你好
// 指定文本编码为 UTF-8,并使用 Base64 编码数据,显示正确:你好
data:text/plain;charset=UTF-8;base64,5L2g5aW9
- 其它类型,例如图片
data:image/jpeg;base64,iVBORw0KGgoAAAANSUhE...
2、Base64
(1)介绍
Base64 是一种基于二进制的编码协议
它的主要目的是用六十四个可打印字符表示所有数据(包括中文、二进制等)
在默认情况下,这六十四个可打印字符包括 [A-Z|a-z|0-9|+|/]
(2)原理
Base64 的编码过程很简单,下面我们直接用一个例子来进行讲解
原数据:Hello,原数据二进制:01001000 01100101 01101100 01101100 01101111
1、将原数据,每 3 个字节分为 1 组,这样就能得到多组 24 个二进制位
(01001000 01100101 01101100) (01101100 01101111)
如果不足 24 个二进制位,后面会讲如何处理
2、将每组 24 个二进制位,每 6 个二进制位分为 1 个小组,这样每个大组包含 4 个小组
(010010 000110 010101 101100) (011011 000110 1111)
如果不足 6 个二进制位,后面补 0;如果不足一组 6 个二进制位,用 = 代替
(010010 000110 010101 101100) (011011 000110 111100 =)
3、对每组 6 个二进制位,在前面添加两个二进制位 00,这样每个大组包含 4 个字节
(00010010 00000110 00010101 00101100) (00011011 00000110 00111100 =)
4、最后对照编码表,查询每个字节对应的编码,就能得到原数据的 Base64 编码
SGVsbG8=
实际上编码表也很简单,如下:
二进制字节 00000000 ~ 00011001 对应编码 A ~ Z
二进制字节 00011010 ~ 00110011 对应编码 a ~ z
二进制字节 00110100 ~ 00111101 对应编码 0 ~ 9
二进制字节 00111110 对应编码 +
二进制字节 00111111 对应编码 /
(3)应用
Web API 中有编码和解码 Base64 数据的方法,分别是 btoa()
和 atob()
btoa()
:编码,对数据进行 Base64 编码atob()
:解码,对经过 Base64 编码后的数据解码
let originalStr = 'Hello'
let encodedData = window.btoa(originalStr)
let decodedData = window.atob(encodedData)
console.log(encodedData) // SGVsbG8=
console.log(decodedData) // Hello
3、Blob
(1)Blob
Blob
对象表示一个不可变、原始数据的类文件对象(常用于表示二进制数据),它的构造函数如下:
Blob(array, options)
array
:一个由ArrayBuffer
、Blob
、DOMString
等对象构成的Array
options
:对象类型,包含一些可选属性,包括type
:表示 MIME 类型,默认为""
endings
:指定包含行结束符\n
的字符串如何被写入,默认为transparent
Blob
对象具有以下属性和方法:
size
:只读属性,表示数据的大小type
:只读属性,表示数据的 MIME 类型text()
:返回一个 Promise,包含 Blob 所有内容的 UTF-8 格式的 USVStringarrayBuffer()
:返回一个 Promise,包含 Blob 所有内容的 Binary 格式的 ArrayBuffer
(2)File
File
对象是一个特殊的 Blob
,我们通常会在用 <input>
标签上传文件的场景遇到这个对象
它保存着文件的描述信息以及文件的原始数据,除了继承 Blob
的属性外,File
还具有以下的属性:
size
:只读属性,表示数据的大小type
:只读属性,表示数据的 MIME 类型name
:只读属性,表示文件的名称lastModified
:只读属性,表示文件的最后修改时间,自 UNIX 时间起始值以来的毫秒数lastModifiedDate
:只读属性,表示文件的最后修改时间,Date
对象
4、BlobURL
可以用 URL.createObjectURL()
给 Blob 等对象创建一个 URL,之后通过这个 URL 可以访问原始数据
但是需要注意的是,这些 URL 对象是不会自动释放的,只有当 document 卸载时才会同时释放这些对象
当然我们还有更好的选择,那就是当我们不再需要这些对象时,通过 URL.revokeObjectURL()
主动释放
5、DataURL 与 Blob 的转化
(1)DataURL -> Blob
function dataURLtoBlob(dataURL) {
return new Promise((resolve, reject) => {
let [descString, dataString] = dataURL.split(',')
let mimeType = descString.split('data:')[1].split(';base64')[0]
let byteData = window
? window.atob(dataString)
: Buffer.from(dataString, 'base64').toString('binary')
let intArray = new Uint8Array(new ArrayBuffer(byteData.length))
for (let idx = 0; idx < byteData.length; idx++) {
intArray[idx] = byteData.charCodeAt(idx)
}
let blob = new Blob([intArray], {type: mimeType})
resolve(blob)
})
}
(2)Blob -> DataURL
function blobToDataURL(blob) {
return new Promise((resolve, reject) => {
blob.arrayBuffer().then((arrayBuffer) => {
let mimetype = blob.type
let intArray = new Uint8Array(arrayBuffer)
let byteData = [].map.call(intArray, (val) => {
return String.fromCharCode(val)
}).join('')
let dataString = window
? window.btoa(byteData)
: Buffer.from(byteData).toString('base64')
let descString = 'data:' + mimetype + ';base64'
let dataURL = descString + ',' + dataString
resolve(dataURL)
})
})
}
注意,在上面的解决方案中,blob.arrayBuffer()
的兼容性其实并不是很好
事实上,我们有一个更加简单的解决方案
function blobToDataURL(blob) {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.onloadend = function() {
resolve(reader.result)
}
reader.readAsDataURL(blob)
})
}
6、图片预览
(1)BlobURL
<!DOCTYPE html>
<html>
<head>
<script>
function bindEvent() {
const input = document.querySelector('input[type=file]')
input.addEventListener('change', uoloadFile)
}
function uoloadFile(e) {
let files = e.target.files;
if (files.length > 0) {
let file = files[0]
document.querySelector('#avatar').src = URL.createObjectURL(file)
}
}
</script>
</head>
<body onload="bindEvent()">
<input type="file" accept="image/png, image/jpeg" />
<image id="avatar" />
</body>
</html>
(2)DataURL
<!DOCTYPE html>
<html>
<head>
<script>
function bindEvent() {
const input = document.querySelector('input[type=file]')
input.addEventListener('change', uoloadFile)
}
function uoloadFile(e) {
let files = e.target.files;
if (files.length > 0) {
let file = files[0]
blobToDataURL(file).then((dataURL) => {
document.querySelector('#avatar').src = dataURL
})
}
}
function blobToDataURL(blob) {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.onloadend = function() {
resolve(reader.result)
}
reader.readAsDataURL(blob)
})
}
</script>
</head>
<body onload="bindEvent()">
<input type="file" accept="image/png, image/jpeg" />
<image id="avatar" />
</body>
</html>
(3)实现效果
【 阅读更多 JavaScript 系列文章,请看 JavaScript学习笔记 】