JS学习笔记——前端关于File、Blob、FileReader和ArrayBuffer的相关概念知识

1、前提

这几天在开发过程中,遇到了下载excel文件问题,其中服务端返回二进制文件流,需要前端自己对二进制文件流进行转换,用到了new Blob()方式,便上网查阅相关资料。

2、Blob对象

2.1、概念介绍

Blob 全称:Binary Large Object(二进制大型对象)

Blob 对象是一个前端的一个专门用于支持文件操作二进制对象,表示一个二进制文件数据内容,表示一个不可变原始数据的类文件对象。通常用来读写文件,比如一个图片文件的内容就可以通过 Blob 对象读写。Blob 对象。Blob 表示的不一定是JavaScript原生格式的数据。

而在前端工程中,我们通常在下面方式获得Blob对象:
在这里插入图片描述
(图片源于知乎@澎湖湾)

2.2、Blob() 函数的参数

浏览器原生提供Blob() 构造函数,用来生成实例。Blob 的内容由参数数组中给出的值的串联组成。

new Blob(array[, options])

Blob构造函数接受两个参数

  • 第一个参数(必填):是一个包含实际数据的数组,数组的成员可以是字符串二进制对象,表示新生成的Blob实例对象的内容。(成员可以是一个由 ArrayBuffer, ArrayBufferView , Blob , DOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings会被编码为UTF-8)
  • 第二个参数(可选):是一个配置对象,表示数据的MIME 类型。options包含两个属性:typeendings,默认空字符串。这里介绍常用的属性 type
let new_blob = new Blob(['hello world'], {type : 'text/html'})

这里data :一个包含 DOMString 的数组
这里options :指定数据类型为 text/html
new_blob是一个blob实例
const obj = { hello: 'wang' };
const blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'})

这里data :一个包含 JSON数据 的数组
这里options :指定数据类型为 application/json
new_blob是一个blob实例

2.3、blob实例的属性

blob 对象也具有两个实例属性

  • size:blob对象的数据大小,即文件的大小,单位为字节
  • type:blob对象所包含数据的MIME类型。如果类型无法确定,则返回空字符串
console.log(new Blob(['hello world'], {type : 'text/html'}))
//Blob {size: 11, type: "text/html"}

let obj = {name:'wang'};
let new_blob = new Blob([ JSON.stringify(obj) ], {type : 'application/json'})
console.log(new_blob)
//Blob {size: 15, type: "application/json"}

2.4、blob实例的方法

blob 对象可以通过slice方法得到一个新的 blob对象

const newBlob = oldBlob.slice([start [, end [, contentType]]])

slice 方法接收三个可选参数

  • startend 都是数值,表示截取的范围
  • contentType 指定截取的内容的 MIME 类型
  • 返回一个新的 Blob对象
var blob = new Blob(['hello world'], {type: 'text/plain'});
console.log(blob.size);  //11

var newBlob = blob.slice(3, 7, 'text/plain');
console.log(newBlob.size); //4

3、File对象

3.1、概念介绍

在JS中,有两个构造函数:FileBlob,而File继承了所有Blob的属性。所以在我们看来,File对象可以看作一种特殊的Blob对象,继承了 blob 的功能并将其扩展使其支持用户系统上的文件

通常情况下,在前端工程中,我们通常在下面方式获得File对象:

  • File 对象是来自用户在一个 元素上选择文件后返回的 FileList 对象
  • 来自由拖放操作生成的 DataTransfer 对象
    图片源于知乎@澎湖湾

3.2、File函数的参数

我们接触的多数关于 File 的操作都是读取,js也为我们提供了手动创建 File 对象的构造函数:

File(bits, name[, options])。
  • 参数一bits (必须):参数的类型必须是ArrayBufferArrayBufferViewBlob,或者 Array[string] 或者任何这些对象的组合。这是 UTF-8 编码的文件内容。
  • 参数二name(必须):参数的类型为字符串String,表示文件名称,或者文件路径
  • 参数三options(可选):参数的类型为对象。表示选项对象,包含文件的可选属性:type 和 lastModified。可用的选项如下:
    (1)type------ string, 表示将要放到文件中的内容的MIME类型。默认值为 空字符串
    (2)lastModified ------ 数值,表示文件最后修改时间的 Unix 时间戳(毫秒)。默认值为 Date.now()
var file = new File(['text1', 'text2'], 'test.txt', {type: 'text/plain'});
console.log('--file1--',file)

// File { lastModified: 1631434865697
//		lastModifiedDate: Sun Sep 12 2021 16:21:05 GMT+0800 (中国标准时间) {}
//		name: "test.txt"
//		size: 10
//		type: "text/plain"
//		webkitRelativePath: ""
//    }

根据已有的 blob 对象创建 File 对象:

var content1 = ['Hello world'];
var blob1 = new Blob(content1, {type: 'text/plain'});
console.log('--blob1--',blob1)
var file2 = new File([blob1], 'test.png', {type: 'image/png'});
console.log('--file2--',file2)
//--blob1-- △ Blob {size: 11, type: "text/plain"}
//--file2-- △ File {lastModified: 1631435132101
//              lastModifiedDate: Sun Sep 12 2021 16:25:32 GMT+0800 (中国标准时间) {}
//              name: "test.png"
//              size: 11
//              type: "image/png"
//              webkitRelativePath: ""}

3.3、File实例的属性

File 对象的实例内容不可见,但是有以下属性可以访问:

属性名称 读 / 写 描述
name 只读 返回文件的名称,由于安全原因,返回的值并不包含文件路径
type 只读 返回 File 对象,表示文件的媒体类型(MIME)。例如 PNG 图像是 “image/png”
size 只读 返回文件的大小
lastModified 只读 返回number,返回所引用文件最后修改日期,自 1970年1月1日0:00 以来的毫秒数
lastModifiedDate 只读 返回Date,返回当前文件的最后修改日期,如果无法获取到文件的最后修改日期,则使用当前日期来替代
webkitRelativePath 只读 返回 File 相关的path 或 URL

示例:

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
head>
<body>
    <div>
        <input type="file" id='f' />
    div>
    <script>
        document.getElementById('f').addEventListener('change', function(event){
            console.log('-this.files-',this.files)
            const file = this.files[0];
            if (file) {
                console.log('file.name',file.name);
                console.log('file.size',file.size);
                console.log('file.lastModified',file.lastModified);
                console.log('file.lastModifiedDate',file.lastModifiedDate);
            }
        });
    script>
body>
html>

这是我们选择一张图片之后,控制台打印情况
在这里插入图片描述
备注

  • 基于当前的实现,浏览器不会实际读取文件的字节流,来判断它的媒体类型,它基于文件扩展来假设
  • 将 PNG 图像文件的后缀名重命名为 .txt,那么读取的该文件的 type 属性值为 “text/plain”, 而不是 “image/png”
  • file.type 仅仅对`常见文件类型可靠。例如图像、文档、音频和视频。不常见的文件扩展名会返回空字符串。开发者最好不要依靠这个属性,作为唯一的验证方案

3.4、File实例的方法

File 对象没有定义额外的方法,由于继承了 Blob 对象,也就继承了 slice方法,用法同上文 Blob 的 slice 方法。

slice([start[, end[, contentType]]])

slice 方法接收三个可选参数

  • startend 都是数值,表示截取的范围
  • contentType 指定截取的内容的 MIME 类型
  • 返回一个新的 Blob对象

FileReaderURL.createObjectURL()createImageBitmap() ,和XMLHttpRequest.send() 都能处理 Blob 和 File。

var file1 = new File(['text1', 'text2'], 'test.txt', {type: 'text/plain'});
console.log(file1)
//△File{
//    lastModified: 1631437933620
//    lastModifiedDate: Sun Sep 12 2021 17:12:13 GMT+0800 (中国标准时间) {}
//    name: "test.txt"
//    size: 10
//    type: "text/plain"
//    webkitRelativePath: ""
//}
var file2 = file1.slice(3,6,'text/plain')
console.log(file2)
//△Blob {
//  size: 3, 
//  type: "text/plain"
//}

4、FileReader对象

4.1、概念介绍

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 FileBlob 对象指定要读取的文件或数据。

其中 File 对象,可以是来自用户在一个 元素上选择文件后返回的 FileList,也可以来自拖放操作生成的 DataTransfer 对象,还可以是来自在一个 HTMLCanvasElement 上执行 mozGetAsFile() 方法后返回结果。

4.2、FileReader构造函数

var reader = new FileReader()

new FileReader()构造函数不需要传入参数,返回一个 FileReader 的实例。FileReader 继承 EventTarget对象。

4.3、FileReader实例的属性

FileReader实例属性

属性名称 读/写 描述
error 只读 DOMException 的实例,表示在读取文件时发生的错误
result 只读 表示文件的内容,该属性仅在读取操作完成后(load)后才有效,格式取决于读取方法
readyState 只读 表示读取文件时状态的数字

备注:readeyState的取值如下:

  • 0 ------ EMPTY --------- 还没有加载任何数据
  • 1 ------ LOADING ------ 数据正在被加载
  • 2 ------ DONE ----------- 已完成全部的读取请求
var reader = new FileReader();
console.log(reader.error);       // null
console.log(reader.result);      // null
console.log(reader.readyState);  // 0
console.log(reader.EMPTY);       // 0
console.log(reader.LOADING);     // 1
console.log(reader.DONE);        // 2

EMPTY、LOADING、DONE 这三个属性同时存在于 FileReader 和它的的原型对象上,因此实例上有这三个属性,FileReader 对象本身也有这三个属性:

console.log(FileReader.EMPTY);   // 0
console.log(FileReader.LOADING); // 1
console.log(FileReader.DONE);    // 2

4.4、FileReader事件

文件的读取是一个异步的过程,和 XMLHttpRequest 对象一样,在读取操作过程中会触发一系列事件。

事件名称 描述 使用示例
abort 读取操作被中断时触发 reader.onabort = function(event) {}
error 在读取操作发生错误时触发 reader.onerror = function(event) {}
load 读取操作完成时触发 reader.addEventListener(‘load’, function(event) {})
loadstart 读取操作开始时触发 reader.onloadstart = function(event) {}
loadend 读取操作结束时(要么成功,要么失败)触发 reader.onloadend = function(event) {}
progress 在读取Blob时触发 reader.onprogress = function(event) {}

4.5、FileReader实例方法

关于FileReader实例的方法,具有以下几种:

方法名称 描述 使用示例
abort() 中止读取操作。只有当 readyState 为 1 时才能调用;调用后,readyState 值为 2 在返回时,readyState属性为DONE。 reader.abort()
readAsArrayBuffer(blob) 读取指定的 BlobFile 对象中的内容。读取操作完成后,(触发loadend事件),result属性中保存的是被读取文件的 ArrayBuffer 数据对象,表示所读取的文件的数据内容 reader.readAsArrayBuffer(blob)
readAsDataURL(blob) 读取指定的 BlobFile 对象中的内容。读取操作完成后,(触发loadend事件),result属性中将保存一个 data:URL格式(base64编码字符串) ,以表示所读取文件的数据内容 reader.readAsArrayBuffer(file)
readAsBinaryString(blob) 已废弃,用 readAsArrayBuffer 代替 –
readAsText(blob[, encoding]) 读取指定的 BlobFile 对象中的内容,将 Blob 或者 File 对象根据特殊的编码格式转化为特定内容(字符串形式),默认编码是 utf-8 。读取完成后,result属性中将包含一个字符串,以表示所读取的文件内容 reader.readAsArrayBuffer(blob)

【读取本地图片示例】

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>
        <input type="file" id='file' accept="image/png, image/jpg, image/jpeg, image/gif" />
        <img src="" alt="Image preview...">
    </div>
    <script>
        var preview = document.querySelector('img');
        var reader  = new FileReader();
        document.getElementById('file').addEventListener('change', function (event) {
            console.log('--files--',this.files[0])
            var file = this.files[0];
            if (file) {
                reader.readAsDataURL(file);
            }
        });
        console.log('---reader--222---',reader)
        reader.addEventListener("load", function () {
            console.log('---reader.result---',reader.result)
            preview.src = reader.result;
        }, false);
    </script>
</body>
</html>

  • 可以看出,当我们选择完某张图片后,FileReader的实例reader的result属性值已经通过readAsDataURL方法,变成了一个data: URL格式的Base64字符串(截图中没有展示完整)
  • dataURL是base64编码的数据格式,展示类型为字符串,形如: data:image/jpeg;base64,/9j/4QXERXhpZgAATU······

对于上面的示例,还可以使用URL.createObjectURL的方法

objectURL = URL.createObjectURL(object)

其中:

  • 参数object:指用于创建 URLFile 对象Blob 对象或者 MediaSource 对象
  • 返回值objectURL:一个DOMString,包含了一个对象URL,该URL可用于指定源 object的内容
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>
        <input type="file" id='file' accept="image/png, image/jpg, image/jpeg, image/gif" />
        <br>
        <img src="" alt="Image preview...">
    </div>
    <script> 
        var preview = document.querySelector('img');
        var reader  = new FileReader();
        document.getElementById('file').addEventListener('change', function (event) {
            console.log('--files--',this.files[0])
            var file = this.files[0];
            if (file) {
                console.log('--url--',URL.createObjectURL(file))
                preview.src = URL.createObjectURL(file);
            }
        });
        
    </script>
</body>
</html>

在这里插入图片描述

5、ArrayBuffer对象

5.1、概念介绍

ArrayBuffer 对象用来表示通用的固定长度原始二进制数据缓冲区,是ES6 才纳入正式 ECMAScript 规范。

ArrayBuffer 类型化数组,类型化数组是JavaScript操作二进制数据的一个接口。最初为了满足JavaScript与显卡之间大量的、实时的数据交换,它们之间的数据通信必须是二进制的,而不能是传统的文本格式的背景下诞生的

ArrayBuffer 对象代表储存二进制数据的一段内存,它不能直接读写只能通过视图(TypedArray视图DataView视图)来读写,它们(视图)会将缓冲区中的数据用特定的格式进行解读,并通过这些格式来读写缓冲区的内容。

5.2、ArrayBuffer函数

浏览器原生提供 ArrayBuffer() 构造函数,用来生成实例

new ArrayBuffer(length)
  • 参数length:整数,表示二进制数据占用的字节长度
  • 返回值:一个指定大小ArrayBuffer 对象,其内容被初始化为 0。
let aa = new ArrayBuffer()
console.log(aa)
//下面为打印内容
ArrayBuffer(0)
byteLength: 0
__proto__: ArrayBuffer
[[Int8Array]]: Int8Array(0)
[[Uint8Array]]: Uint8Array(0)
[[Int16Array]]: Int16Array(0)
[[Int32Array]]: Int32Array(0)
[[ArrayBufferByteLength]]: 0
[[ArrayBufferData]]: "0x000000000000"
let aa = new ArrayBuffer(10)//实例对象 buffer 占用 10 个字节
console.log(aa)
//下面为打印内容
ArrayBuffer(10)
byteLength: (...)
__proto__: ArrayBuffer
[[Int8Array]]: Int8Array(10)
[[Uint8Array]]: Uint8Array(10)
[[Int16Array]]: Int16Array(5)
[[ArrayBufferByteLength]]: 10
[[ArrayBufferData]]: "0x002400a6a2b0"

5.3、ArrayBuffer实例的属性

ArrayBuffer 对象有实例属性 byteLength ,表示当前实例占用的内存字节长度(单位字节),一单创建就不可变更(只读`):

const buffer = new ArrayBuffer(32);
buffer.byteLength; // 32

5.4、ArrayBuffer实例的方法

ArrayBuffer.prototype.slice()

ArrayBuffer的实例有一个slice方法,允许将内存区域的一部分,拷贝生成一个新的ArrayBuffer对象。

slice(start , end)的参数:

  • start:整数类型,表示开始复制的位置。默认从 0 开始
  • end:整数类型,表示结束复制的位置(不包括结束的位置)。如果省略,则表示复制到结束。
const buff = new ArrayBuffer(32);
console.log(buff.byteLength);//32
const newBuffer = buff.slice(0, 3);
//以下为打印内容
ArrayBuffer(3)
byteLength: (...)
__proto__: ArrayBuffer
[[Int8Array]]: Int8Array(3)
[[Uint8Array]]: Uint8Array(3)
[[ArrayBufferByteLength]]: 3
[[ArrayBufferData]]: "0x002400a683b0"

除了slice方法,ArrayBuffer对象不提供任何直接读写内存的方法,只允许在其上方建立视图,然后通过视图读写

5.5、ArrayBuffer静态方法

ArrayBuffer.isView()

ArrayBuffer本身有一个静态方法isView,返回一个布尔值,表示参数是否为ArrayBuffer的视图实例。这个方法大致相当于判断参数是否为TypedArray实例或DataView实例

const buffer = new ArrayBuffer(8);
ArrayBuffer.isView(buffer) // false

const v = new Int32Array(buffer);
ArrayBuffer.isView(v) // true

5.6、DataView视图和TypedArray视图

为了读写ArrayBuffer的实例,需要为它指定视图

ArrayBuffer对象作为内存区域,可以存放多种类型的数据。同一段内存,不同数据有不同的解读方式,这就叫做“视图”(view)

ArrayBuffer有两种视图,

  • TypedArray视图:数组成员都是同一个数据类型,TypedArray视图是用来向网卡、声卡之类的本机设备传送数据,所以使用本机的字节序就可以了
  • DataView视图:数组成员可以是不同的数据类型,DataView视图是用来处理网络设备传来的数据,所以大端字节序或小端字节序是可以自行设定的
  • 两者的区别主要是字节序

DataView实例提供8个方法读取内存

getInt8:读取1个字节,返回一个8位整数。
getUint8:读取1个字节,返回一个无符号的8位整数。
getInt16:读取2个字节,返回一个16位整数。
getUint16:读取2个字节,返回一个无符号的16位整数。
getInt32:读取4个字节,返回一个32位整数。
getUint32:读取4个字节,返回一个无符号的32位整数。
getFloat32:读取4个字节,返回一个32位浮点数。
getFloat64:读取8个字节,返回一个64位浮点数。
var buf = new ArrayBuffer(32);
var dataView = new DataView(buf);
dataView.getUint8(0) // 0

同样,TypedArray对象一共提供9种类型的视图,每一种视图都是一种构造函数。

Int8Array:8位有符号整数,长度1个字节。
Uint8Array:8位无符号整数,长度1个字节。
Uint8ClampedArray:8位无符号整数,长度1个字节,溢出处理不同。
Int16Array:16位有符号整数,长度2个字节。
Uint16Array:16位无符号整数,长度2个字节。
Int32Array:32位有符号整数,长度4个字节。
Uint32Array:32位无符号整数,长度4个字节。
Float32Array:32位浮点数,长度4个字节。
Float64Array:64位浮点数,长度8个字节。
var buffer = new ArrayBuffer(12);

var x1 = new Int32Array(buffer);
x1[0] = 1;
var x2 = new Uint8Array(buffer);
x2[0]  = 2;

x1[0] // 2

上面代码对同一段内存,分别建立两种视图:32位带符号整数(Int32Array构造函数)和8位不带符号整数(Uint8Array构造函数)。由于两个视图对应的是同一段内存,一个视图修改底层内存,会影响到另一个视图

对于DataView视图和TypedArray视图更详细的讲解,可以参考博客:

  • ES6学习笔记(二十二)ArrayBuffer
  • 二进制数组 ArrayBuffer

6、Blob和ArrayBuffer转换

ArrayBuffer 与 Blob 区别:

  • Blob 用于操作二进制文件
  • ArrayBuffer 用于操作内存
  • ArrayBuffer是原始的二进制数据缓冲区不能设置MIME类型;Blob可以储存大量的二进制编码格式的数据,可以设置对象的MIME类型
  • ArrayBuffer的数据,是可以按照字节去操作的,而Blob的只能作为一个整的对象去处理。ArrayBuffer相比Blob更接近真实的二进制,更底层

6.1、Blob 转 ArrayBuffer:

此处需要借助fileReader对象:

let blob = new Blob([1,2,3,4])
let reader = new FileReader();
reader.onload = function(result) {
    console.log(result);
}
reader.readAsArrayBuffer(blob);

在这里插入图片描述

6.2、ArrayBuffer 转 Blob

arraybuffer转blob很方便,作为参数传入就行了

var buffer = new ArrayBuffer(16)
var blob = new Blob([buffer])

在这里插入图片描述


本文部分参考博客:

  • JavaScript 文件对象详解
  • ArrayBuffer和Blob
  • 前端 ArrayBuffer 与 Blob 互转
  • JS中的Blob和ArrayBuffer

你可能感兴趣的:(JavaScript,React,javascript,html5,html)