JavaScript中的File和Blob

121 阅读5分钟

文件的二进制本质

计算机是二进制的世界,无论是文本文件,图片文件还是视频文件音频文件,底层都是以二进制的形式存储的。如果我们想探究文件底层的二进制到底是什么样的,可以借助VS codeHex Editor插件来查看。这里演示两种文件的底层二进制,一种是txt文本文件,一种是PNG图片文件。

首先看一下文本文件里面的字符串,几个简单字母和数字:

选中文件,右键->Open With->Hex Editor,以十六进制打开文件的内容,这里之所以用十六进制显示,是因为一个十六进制数字等价于四个二进制数字,一个十六进制数字比四个二进制数字更易阅读。

这里我将鼠标点到字母c上,可以看到它对应的十六进制数字是63,这个其实就是小写字母c对应的十六进制ASCII码。下面截取了一部分ASCII码作为参考:

二进制八进制十进制十六进制字符解释
0110 0001141970x61a小写字母a
0110 0010142980x62b小写字母b
0110 0011143990x63c小写字母c
0110 01001441000x64d小写字母d
0110 01011451010x65e小写字母e
0110 01101461020x66f小写字母f
0110 01111471030x67g小写字母g

看完了文本文件,再来用Hex Editor查看一个PNG文件的内容。图片文件原始内容如下:

使用Hex Editor查看文件的十六进制:

这个PNG文件的二进制内容主要分为四部分:

  • 前8个字节是文件魔数,表示这个文件是一个PNG文件,

  • 第9至33个字节是IHDR(Image Header),文件头数据块,包含文件的基本信息和结构信息,

  • 第34至2773字节是IDAT(Image Data),图像数据块,存储实际的图像数据,

  • 第2774字节至文件结束是IEND(Image END),图像结束数据块,用来标记图像结束。

如果想要知道PNG文件都有哪些文件块,可以借助www.nayuki.io/page/png-fi… 网站查看。

Blob和File

Blob(Binary Large Object)JavaScript中表示一个不可变的、原始数据的类文件对象,原始数据指的就是二进制数据。

FileJavaScript中表示文件数据的对象,它继承自Blob并且扩展了文件名,最后修改时间等信息。

这里先分别创建一个Blob对象和File对象,点击按钮之后在浏览器控制台打印这两个对象的信息。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
</head>
<body>
<div id="app">
    <el-button :plain="true" @click="createFileAndBlob">创建</el-button>


</div>
<script>
    new Vue({
        el: '#app',
        methods: {
            async createFileAndBlob() {
                let blob = new Blob(['123']);
                console.log(blob)
                let file = new File(['123'], '123.txt');
                console.log(file)
                console.log(`blob原型和file原型的原型是否相等: ${Object.getPrototypeOf(blob)===Object.getPrototypeOf(Object.getPrototypeOf(file))}`);            }
        }
    })
</script>
</body>
</html>

Blob对象

File对象

从原型链也可以看出,File的原型的原型是Blob。打印结果也说明了这个关系:

blob原型和file原型的原型是否相等: true

arrayBuffer方法

arrayBuffer方法返回一个二进制数据的ArrayBufferblobfile都可以调用,修改一下前面的createFileAndBlob方法,把blobfilearrayBuffer结果打印出来。

async createFileAndBlob() {
    let blob = new Blob(['123']);
    let file = new File(['123'], '123.txt');
    console.log(await blob.arrayBuffer())
    console.log(await file.arrayBuffer())
}

从浏览器控制台查看结果,两个arrayBuffer基本一致。其实file对象身上并没有arrayBuffer方法,它调用的是原型链上BlobarrayBuffer方法,所以两个arrayBuffer的返回结果才会如此的相似。

arrayBuffer[[Int8Array]]存储了字节数据的具体信息,49、50、51表示的是字符1、2、3的十进制ASCII码,具体可以参考下面:

二进制八进制十进制十六进制字符解释
0010 1111057470x2F/斜杠
0011 0000060480x300字符0
0011 0001061490x311字符1
0011 0010062500x322字符2
0011 0011063510x333字符3
0011 0100064520x344字符4

通过FileReader获取ArrayBuffer

FileReader对象可以读取文件内容,创建一个FileReader对象,调用readAsArrayBuffer方法来读取文件对象,当文件内容读取完毕之后就会进入onloadend回调,在回调函数中可以通过事件的taeget.result来获取ArrayBuffer

async createFileAndBlob() {
    let file = new File(['123'], '123.txt');
    const reader = new FileReader();
    reader.onloadend = (event) => {
        console.log(event.target.result)
    }
    reader.readAsArrayBuffer(file);
}

DataView

ArrayBuffer中的数据只能查看,不能修改,如果想要修改数据,可以使用DataView。它是一个可以从二进制 ArrayBuffer 对象中读写多种数值类型的底层接口。

修改之前先看一下原始的数据:

let file = new File(['123'], '123.txt');
let arrayBuffer = await file.arrayBuffer();
console.log(arrayBuffer);

修改第二个字节的内容:

let file = new File(['123'], '123.txt');
let arrayBuffer = await file.arrayBuffer();
let dv = new DataView(arrayBuffer);
//  修改第二个字节,改成57,
dv.setInt8(1, 57);
console.log(arrayBuffer);

可以看到ArrayBuffer中第二个字节已经变成57了。