文章目录
File、Blob、FileReader、ArrayBuffer、Base64
JavaScript 提供了一些 API 来处理文件或原始文件数据,例如:File、Blob、FileReader、ArrayBuffer、base64 等。
Blob
Blob
【Binary large object】即二进制大对象,表示原始文件的数据。它表示一个不可变、原始数据的类文件对象。
Blob创建
new Blob(array, options)
array
:由 ArrayBuffer
、ArrayBufferView
、Blob
、DOMString
等对象构成的,将会被放进 Blob
;如果省略该参数,则创建一个空的 Blob。
options
:可选的 BlobPropertyBag
字典,它可能会指定如下两个属性
type
:默认值为 “”,表示将会被放入到blob
中的数组内容的 MIME 类型。endings
:默认值为"transparent
",用于指定包含行结束符\n
的字符串如何被写入,不常用。
常见的 MIME 类型如下:
MIME 类型 | 描述 |
---|---|
text/plain | 纯文本文档 |
text/html | HTML 文档 |
text/javascript | JavaScript 文件 |
text/css | CSS 文件 |
application/json | JSON文件 |
application/pdf | PDF文件 |
application/xml | XML 文件 |
image/jpeg | JPEG图像 |
image/png | PNG图像 |
image/gif | GIF 图像 |
image/svg+xml | SVG 图像 |
audio/mpeg | MP3 文件 |
video/mpeg | MP4 文件 |
简单的例子:
const blob = new Blob(["Hello World"], {type: "text/plain"});
实例属性
这个 blob 对象上有两个属性:
size
:Blob对象中所包含数据的大小(字节);type
:字符串,认为该Blob对象所包含的 MIME 类型。如果类型未知,则为空字符串。
打印结果为:
const blob = new Blob(["Hello World"], {type: "text/plain"});
console.log(blob.size); // 11
console.log(blob.type); // "text/plain"
可以将Blob对象用URL.createObjectURL() 方法将将其转化为一个 URL:
<iframe></iframe>
const iframe = document.getElementsByTagName("iframe")[0];
const blob = new Blob(["Hello World"], {type: "text/plain"});
iframe.src = URL.createObjectURL(blob);
Blob切片
Blob 对象内置了 slice() 方法用来将 blob 对象分片,切片后相当于从Blob中创建Blob,语法如下:
const blob = instanceOfBlob.slice([start [, end [, contentType]]]};
"["表示可选项;其有三个参数:
start
:设置切片的起点,即切片开始位置。默认值为 0,这意味着切片应该从第一个字节开始;如果你传入的是一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。例如,-10 将会是Blob
的倒数第十个字节。end
:设置切片的结束点,会对该位置之前的数据进行切片。默认值为blob.size
;如果你传入了一个负数,那么这个偏移量将会从数据的末尾从后到前开始计算。contentType
:设置新 blob 的 MIME 类型。如果省略 type,则默认为 blob 的原始值。
简单的例子:
const iframe = document.getElementsByTagName("iframe")[0];
const blob = new Blob(["Hello World"], {type: "text/plain"});
const subBlob = blob.slice(0, 5);
iframe.src = URL.createObjectURL(subBlob);
此时页面会显示出 Hello
。
File
File对象
其实就是特殊类型的 Blob,即 Blob 的属性和方法同样适用于 File 对象。
注意:File 对象中只存在于浏览器环境中,在 Node.js 环境中不存在。
JS 中主要有两个地方产生 File 对象:
- 通过
<input type='file'>
元素上传文件后,返回的 FileList 对象 - 文件拖放操作生成的
DataTransfer
对象
input
首先定义一个input
标签:
<input type="file" id="fileInput" multiple="multiple">
const fileInput = document.getElementById("fileInput");
fileInput.onchange = (e) => {
console.log(e.target.files);
}
这里给 input
标签添加了三个属性:
type="file"
:指定input
的输入类型为文件;id="fileInput"
:指定input
的唯一 id;multiple="multiple"
:指定input
可以同时上传多个文件。
给 input
标签添加 onchange
事件,当选择文件并上传之后触发。
当点击上传文件时,控制台就会输出一个 FileList 数组,这个数组的每个元素都是一个 File 对象,一个上传的文件就对应一个 File 对象:
每个 File
对象都包含文件的一些属性,这些属性都继承自 Blob 对象:
lastModified
:引用文件最后修改日期,为自1970年1月1日0:00以来的毫秒数;lastModifiedDate
:引用文件的最后修改日期;name
:引用文件的文件名;size
:引用文件的文件大小;type
:文件的媒体类型(MIME);webkitRelativePath
:文件的路径或 URL。
文件拖放
拖放区域用于响应放置操作并从放置的项目中提取信息。这些是通过 ondrop
和 ondragover
两个 API 实现的。
<div id="drop-zone"></div>
const dropZone = document.getElementById("drop-zone");
dropZone.ondragover = (e) => {
e.preventDefault();
}
dropZone.ondrop = (e) => {
e.preventDefault();
const files = e.dataTransfer.files;
console.log(files)
}
注意:
- 两个拖拽事件中都需要添加
e.preventDefault()
,用来阻止默认事件,可以阻止浏览器的一些默认行为。比如放置文件将显示在浏览器窗口中。 - 当拖放文件到拖放区域时,控制台就会输出一个 FileList 数组,该数组的每一个元素都是一个
File
对象。这个 FileList 数组是从事件参数的dataTransfer
属性的files
获取的:
这里得到的 File
对象和通过 input
标签获得的 File
对象是完全一样的。
FileReader
FileReader 是一个异步 API,用于读取文件并提取其内容以供进一步使用。FileReader 可以将 Blob 读取为不同的格式。
基本使用
创建对象
const reader = new FileReader();
对象常用属性如下:
error
:表示在读取文件时发生的错误;
result
:文件内容。该属性仅在读取操作完成后才有效,数据的格式取决于使用哪个方法来启动读取操作。
readyState
:表示FileReader
状态的数字。取值如下: | 常量名 | 值 | 描述 | | — | — | — | | EMPTY | 0 | 还没有加载任何数据。 | | LOADING | 1 | 数据正在被加载。 | | DONE | 2 | 已完成全部的读取请求。 |
自身方法:
- readAsArrayBuffer():读取指定 Blob 中的内容,完成之后,
result
属性中保存的将是被读取文件的ArrayBuffer
数据对象; - FileReader.readAsBinaryString():读取指定 Blob 中的内容,完成之后,
result
属性中将包含所读取文件的原始二进制数据
; - FileReader.readAsDataURL():读取指定 Blob 中的内容,完成之后,
result
属性中将包含一个data: URL 格式的 Base64 字符串
以表示所读取文件的内容。 - FileReader.readAsText():读取指定 Blob 中的内容,完成之后,
result
属性中将包含一个字符串
以表示所读取的文件内容。
可以看到,上面这些方法都接受一个要读取的 blob 对象作为参数,读取完之后会将读取的结果放入对象的 result
属性中。
事件处理
abort
:该事件在读取操作被中断时触发;error
:该事件在读取操作发生错误时触发;load
:该事件在读取操作完成时触发;progress
:该事件在读取 Blob 时触发。
当然,这些方法可以加上前置 on 后在HTML元素上使用,比如onload
、onerror
、onabort
、onprogress
。除此之外,由于 FileReader
对象继承自EventTarget
,因此还可以使用 addEventListener()
监听上述事件。
注意: progress
事件提供了两个属性:loaded
(已读取量)和total
(需读取总量)。
简单的例子:
<input type="file" id="fileInput">
const fileInput = document.getElementById("fileInput");
const reader = new FileReader();
fileInput.onchange = (e) => {
reader.readAsText(e.target.files[0]);
}
reader.onload = (e) => {
console.log(e.target.result);
}
首先创建了一个 FileReader
对象,当文件上传成功时,使用 readAsText()
方法读取 File
对象,当读取操作完成时打印读取结果。
当文件是png格式的图片时,会打印出乱码。那该如何处理这种二进制数据呢?readAsDataURL()
是一个不错的选择,它可以将读取的文件的内容转换为 base64 数据的 URL 表示。
通过下面的例子将图片显示在页面上:
<input type="file" id="fileInput" />
<img id="preview" />
const fileInput = document.getElementById("fileInput");
const preview = document.getElementById("preview");
const reader = new FileReader();
fileInput.onchange = (e) => {
reader.readAsDataURL(e.target.files[0]);
};
reader.onload = (e) => {
preview.src = e.target.result;
console.log(e.target.result);
};
当上传大文件时,可以通过 progress
事件来监控文件的读取进度:
const reader = new FileReader();
reader.onprogress = (e) => {
if (e.loaded && e.total) {
const percent = (event.loaded / event.total) * 100;
console.log(`上传进度: ${Math.round(percent)} %`);
}
});
ArrayBuffer
ArrayBuffer
对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 的内容不能直接操作,只能通过 DataView 对象或 TypedArrray 对象来访问。这些对象用于读取和写入缓冲区内容。
ArrayBuffer 本身就是一个黑盒,不能直接读写所存储的数据,需要借助以下视图对象来读写:
- TypedArray:用来生成内存的视图,通过9个构造函数,可以生成9种数据格式的视图。
- DataViews:用来生成内存的视图,可以自定义格式和字节序。
ArrayBuffer与Blob的区别:Blob 作为一个整体文件,适合用于传输 ;当需要对二进制数据进行操作时(比如要修改某一段数据时),就可以使用 ArrayBuffer。
常用方法
new ArrayBuffer()
new ArrayBuffer(bytelength)
ArrayBuffer()
构造函数可以分配指定字节数量的缓冲区,其参数和返回值如下:
- 参数:它接受一个参数,即 bytelength,表示要创建数组缓冲区的大小(以字节为单位。);
- 返回值:返回一个新的指定大小的ArrayBuffer对象,内容初始化为0。
ArrayBuffer.prototype.byteLength
它是一个只读属性,表示 ArrayBuffer 的 byte 的大小,在 ArrayBuffer 构造完成时生成,不可改变。
const buffer = new ArrayBuffer(16);
console.log(buffer.byteLength); // 16
ArrayBuffer.prototype.slice()
该方法可以用来截取 ArrayBuffer 实例,它返回一个新的 ArrayBuffer ,它的内容是这个 ArrayBuffer 的字节副本,从 begin(包括),到 end(不包括)。
const buffer = new ArrayBuffer(16);
console.log(buffer.slice(0, 8)); // 16
ArrayBuffer.isView()
ArrayBuffer.isView()
静态方法判断传入值是否是 ArrayBuffer
视图之一,它的返回值是一个布尔值,如果参数是 ArrayBuffer 的视图实例则返回 true,例如类型数组对象或 DataView 对象;否则返回 false。
// Create an ArrayBuffer with a size in bytes
const buffer = new ArrayBuffer(16);
console.log(ArrayBuffer.isView(new Int32Array()));
// Expected output: true
TypedArray
TypedArray 对象一共提供 9 种类型的视图,每一种视图都是一种构造函数。如下:
元素 | 类型化数组 | 字节 | 描述 |
---|---|---|---|
Int8 | Int8Array | 1 | 8 位有符号整数 |
Uint8 | Uint8Array | 1 | 8 位无符号整数 |
Uint8C | Uint8ClampedArray | 1 | 8 位无符号整数 |
Int16 | Int16Array | 2 | 16 位有符号整数 |
Uint16 | Uint16Array | 2 | 16 位无符号整数 |
Int32 | Int32Array | 4 | 32 位有符号整数 |
Uint32 | Uint32Array | 4 | 32 位无符号整数 |
Float32 | Float32Array | 4 | 32 位浮点 |
Float64 | Float64Array | 8 | 64 位浮点 |
**Uint8Array:**将 ArrayBuffer 中的每个字节视为一个整数,可能的值从 0 到 255 (一个字节等于 8 位)。 这样的值称为“8 位无符号整数”。
**Uint16Array:**将 ArrayBuffer 中任意两个字节视为一个整数,可能的值从 0 到 65535。 这样的值称为“16 位无符号整数”。
类型数组与普通数组区别:
- 类型化数组的元素都是连续的,不会为空;
- 类型化数组的所有成员的类型和格式相同;
- 类型化数组元素默认值为 0;
- 类型化数组本质上只是一个视图层,不会存储数据,数据都存储在更底层的 ArrayBuffer 对象中。
new TypedArray()
new Int8Array(length);
new Int8Array(typedArray);
new Int8Array(object);
new Int8Array(buffer [, byteOffset [, length]]);
- TypedArray(length):通过分配指定长度内容进行分配
let view = new Int8Array(16)
- TypedArray(typeArray):接收一个视图实例作为参数
const view = new Int8Array(new Uint8Array(6))
- TypedArray(object):参数可以是一个普通数组
const view = new Int8Array([1, 2, 3, 4, 5])
需要注意,TypedArray视图会开辟一段新的内存,不会在原数组上建立内存。当然,这里创建的类型化数组也能转换回普通数组:
Array.prototype.slice.call(view); // [10, 2, 3, 6, 5]
-
TypeArray(buffer [, byteOffset [, length]]):这种方式有三个参数,其中第一个参数是一个ArrayBuffer对象;第二个参数是视图开始的字节序号,默认从0开始,可选;第三个参数是视图包含的数据个数,默认直到本段内存区域结束。
const buffer = new ArrayBuffer(8); const view1 = new Int32Array(buffer); const view2 = new Int32Array(buffer, 4); console.log(view1, view2);
DataView
DataView 视图是一个可以从 二进制 ArrayBuffer 对象中读写多种数值类型的底层接口,使用它时,不用考虑不同平台的字节序问题。DataView视图提供更多操作选项,而且支持设定字节序。本来,在设计目的上,ArrayBuffer对象的各种TypedArray视图,是用来向网卡、声卡之类的本机设备传送数据,所以使用本机的字节序就可以了;而DataView视图的设计目的,是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的。
new DataView()
DataView视图可以通过构造函数来创建,它的参数是一个ArrayBuffer对象,生成视图。其语法如下:
new DataView(buffer [, byteOffset [, byteLength]])
buffer
:一个已经存在的 ArrayBuffer 对象,DataView 对象的数据源。
byteOffset
:可选,此 DataView 对象的第一个字节在 buffer 中的字节偏移。如果未指定,则默认从第一个字节开始。
byteLength
:可选,此 DataView 对象的字节长度。如果未指定,这个视图的长度将匹配 buffer 的长度。
Object URL
Object URL
(MDN定义名称)又称Blob URL
(W3C定义名称),是HTML5中的新标准。它是一个用来表示File Object 或Blob Object 的URL。
在网页中,我们可能会看到过这种形式的 Blob URL:
src="blob:https://www.youtube.com/fd40f316-424a-4a39-ba85-9f71d9658c2d"
用来创建一个指向 Blob 或 File 对象的可以用作图像、二进制数据下载链接等的 URL 源,可以在 < img /> < script /> 中用当作 src 属性的值。
首先定义一个文件上传的 input 和一个 图片预览的 img:
<input type="file" id="fileInput" />
<img id="preview" />
然后使用URL.createObjectURL()
将FIle对象转化为一个URL:
const fileInput = document.getElementById("fileInput");
const preview = document.getElementById("preview");
fileInput.onchange = (e) => {
preview.src = URL.createObjectURL(e.target.files[0]);
console.log(preview.src);
};
Base64
Base64 是一组相似的二进制到文本(binary-to-text)的编码规则,使得二进制数据在解释成 radix-64 的表现形式后能够用 ASCII 字符串的格式表示出来。Base64 编码普遍应用于需要通过被设计为处理文本数据的媒介上储存和传输二进制数据而需要编码该二进制数据的场景。为了保证数据的完整并且不用在传输过程中修改这些数据。
在 JavaScript 中,有两个函数被分别用来处理解码和编码 base64 字符串:
atob()
:解码,解码一个 Base64 字符串;btoa()
:编码,从一个字符串或者二进制数据编码一个 Base64 字符串。
btoa("JavaScript") // 'SmF2YVNjcmlwdA=='
atob('SmF2YVNjcmlwdA==') // 'JavaScript'
应用场景
基于Data URL
使用toDataURL()
方法把 canvas 画布内容生成 base64 编码格式的图片:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
const dataUrl = canvas.toDataURL();
readAsDataURL()
方法把读取的文件转为base64格式的data URL返回。
)`:解码,解码一个 Base64 字符串;
btoa()
:编码,从一个字符串或者二进制数据编码一个 Base64 字符串。
btoa("JavaScript") // 'SmF2YVNjcmlwdA=='
atob('SmF2YVNjcmlwdA==') // 'JavaScript'
应用场景
基于Data URL
使用toDataURL()
方法把 canvas 画布内容生成 base64 编码格式的图片:
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
const dataUrl = canvas.toDataURL();
readAsDataURL()
方法把读取的文件转为base64格式的data URL返回。