随笔-前端文件操作 File, Blob, ArrayBuffer 以及FileReader

MDN对于Blob介绍是:Blob 对象表示一个不可变、原始数据的类文件对象。

啥叫类文件对象?

Blob翻译过来是 Binary Larger Object,即二进制大对象。即Blob对象的组成是二进制数据。

而我们知道文件也是二进制编码的数据。

那么为什么Blob叫类文件呢,而不是文件呢?

其实Blob在这里代表的是比文件更加底层的概念。

我们知道文件是由二进制数据组成的,那么我们随意编写一些二进制数据,它能代表一个文件吗?答案是不能。文件的二进制数据具有各种特殊标识,来帮助外部理解一堆二进制数据是一个文件。

Blob代表的其实就是文件的切片。

我们知道文件的二进制数据组合在一起才能代表文件,而文件的二进制数据被分段后,比如分成十段,那么每一段都无法表示文件。但是每一段又都是文件的必不可少的组成部分。

此时就需要Blob来表示这些文件切片。所以说Blob是一个类文件对象。它不是文件,但是却和文件有千丝万缕的关系。

在浏览器API中,Blob是File的父类,这很好理解,父类是基类,子类是扩展类,我们可以说文件File是一堆二进制数据,是一个特殊的Blob,它可以当成Blob。但是它又比Blob多了一层含义。

Blob对象的属性:

size只读。Blob 对象中所包含数据的大小(字节)。
type只读。一个字符串,表明该 Blob 对象所包含数据的 MIME 类型。如果类型未知,则该值为空字符串。

有人说Blob对象咋还有文件才有的MIME类型呢?它不就是一个二进制数据对象吗?

还是要再说明一下,Blob对象相当于File对象的切片,如何标识一个文件切片属于什么文件类型是有必要的,它涉及都二进制数据的解析。

Blob对象的方法:

Blob.slice([start[, end[, contentType]]])返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。
Blob.stream()返回一个能读取blob内容的 ReadableStream
Blob.text()返回一个promise且包含blob所有内容的UTF-8格式的 USVString
Blob.arrayBuffer()返回一个promise且包含blob所有内容的二进制格式的 ArrayBuffer 

Blob构造函数

Blob(blobParts[, options])")

返回一个新创建的 Blob 对象,其内容由参数中给定的数组串联组成。

其中blobParts参数是一个数组,数组中的每一项元素连接起来构成Blob对象的数据,数组中的每项元素可以是ArrayBufferArrayBufferViewBlobDOMString 。

options:可选项,字典格式类型,可以指定如下两个属性:

  • type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型。

  • endings,默认值为"transparent",用于指定包含行结束符\n的字符串如何被写入。 它是以下两个值中的一个: “native”,表示行结束符会被更改为适合宿主操作系统文件系统的换行符; “transparent”,表示会保持blob中保存的结束符不变。

File,Blob的slice方法

那么现在了解Blob,而File继承了Blob的所有方法,且进行了拓展,可以直接操作文件,比如文件分片slice

从打印结果可以看出,file经过slice得到文件切片file_cp,而file_cp就不再是File类型了,而是Blob类型。

File,Blob的text方法

file.text()返回一个promise,该promise对象的结果是 blob所有内容的UTF-8格式的 USVString

即file.text()将二进制编码的数据 转成了 utf-8编码的文本字符串。

不知道意义何在…,就像你用Notepad++打开一张png格式的图片一样。

File,Blob的arrayBuffer方法

file.arrayBuffer()

file.arrayBuffer()返回一个promise对象,其结果是 blob所有内容的二进制格式的 ArrayBuffer

ArrayBuffer是啥

那么啥是ArrayBuffer呢?ArrayBuffer - JavaScript | MDN (mozilla.org)")

ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。

它是一个字节数组,通常在其他语言中称为“byte array”。

你不能直接操作 ArrayBuffer 的内容,而是要通过 TypeArray 或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

为什么ArrayBuffer中的数据无法直接操作呢?

因为ArrayBuffer数组的元素都是字节,即八位二进制数,且是二进制编码的,我们无法将ArrayBuffer字节数组的字节元素取出来,也无法覆盖字节元素。因为我们输入的都是字符,而不是字节。

而我们知道八位二进制数 可以转成 其他类型数据,最常见就是十进制数值,而我们是可以输入十进制数值的,所以可以将ArrayBuffer字节数组转为TypeArray类型数组,常见的TypeArray数组有:

由于ArrayBuffer都是字节元素,即每个元素都是无符号8位2进制,所以我们通常是将ArrayBuffer转为Unit8Array来进行处理

此时我们就可以对字节数组转成的Uint8Array数组进行元素操作了

对Uint8Array修改完后,再将其转为ArrayBuffer,则很简单,Uint8Array对象上就有一个buffer属性,可以获取到之前的ArrayBuffer实例

另外TypeArray没有新增,删除元素的操作,因为TypeArray的数组长度是固定的,无法改变的。

所以这么一看ArrayBuffer除了可以转为TypeArray修改字节元素外,也没啥,关键谁没事改字节数啊,文件的字节被改了,可能都无法解析了。

File,Blob的stream方法

file.stream()

返回一个能读取blob内容的 ReadableStream

那么什么是ReadableStream呢?ReadableStream - Web API 接口参考 | MDN (mozilla.org)")

流操作API 中的ReadableStream 接口呈现了一个可读取的二进制流操作。

啥叫二进制流?

我们知道程序上的各种数据在计算机底层上都是二进制形式的,而程序中可识别数据互相传递,就意味着二进制形式数据的传输,即一串010101在各个程序中传输,而这些传输都是有方向性,就像水流一样,总是朝着一个方向流动,不可能出现一股水流出现两个方向。所以二进制数据的传输也是如此,所以二进制数据传输,也叫做流。

那么流的方向有哪些呢?

一个程序需要外部传入数据,那就底层的二进制数据就是朝着该程序输入,也叫输入流。

一个程序向外部提供数据,那么底层二进制数据就是朝程序外输出,也叫输出流。

具体判断是输入流,还是输出流的要点是,数据源是谁,如果从数据源读取数据,那么相当于数据源对外输出,就是输出流,如果向数据源写入数据,那么相当于输入流。

所以一个文件的输出流一般和读操作相关,输入流和写操作相关。

另外有一个关于流的概念,那就是流的组成。

我们知道流是指二进制数据的流动,那么流的组成就是二进制数据。但是二进制数据有不同的形式:

比如 0000000000001111111111111100000000000000,和 00000000 00001111 11111111 11000000 00000000

二者二进制数都相同,都表示40位的二进制数,但是第二个按照8位一组,进行了分组。

我们知道 8 bit = 1 Byte,即8位二进制数 相当于一个字节。

所以在传输中的流有了两个概念,比特(bit)流,和字节(Byte)流,字节流就是将比特流按照8位一组进行分组后的二进制数据。

而在字节之上就是字符了,字符指的就是人类可以识别的文字,而字节是计算机可以识别的文字。

人们制定了各种编码方式,将字节组合起来代表一个人类世界的字符。

比较有名的编码有:ASCII码(英语系编码),GBK码(汉语系编码),UTF-8码(世界所有语言编码)

由于英语只有26字母以及一些常用的英语符号,所以ASCII码只需要一个字节(相当于8位二进制数,可以有255种对应关系)就可以代表所有英语系常用字符。

而汉语有成千上万的文字,即使按照偏旁部首进行分类,也有很多,所以GBK字符需要两个字节(相当于16位二进制数,可以有 2^16 - 1 = 65535种对应关系)

而想要表示世界上所有语言,UTF-8的字符需要三个字节(相当于24位二进制数,即2^24-1种对应关系。)

而无论操作系统,还是浏览器,还是程序编写工具,还是记事本,它们都是直接面向人类的,所以他们需要将字节 按照对应编码规则 转化为 字符。

所以字节流  在进行分组,即按照编码规则分组,比如UTF-8编码,将三个字节分为一组,形成字符流。

而无论是比特流,还是字节流,还是字符流,它们都有流向,即可以分为输入流和输出流。

如果是基于文件的读取和写入的话,那么也可以叫做 读取流(输出流) 和 写入流(输入流)。

ReadableStream 是啥

那么ReadableStream 接口呈现了一个可读取的二进制流操作。

其实就是说 以字节流的方式读取文件中二进制数据 的 操作

ReadableStream的属性

locked只读。locked 返回这个可读流是否被一个读取器锁定

ReadableStream对象的方法

cancel取消读取流,读取方发出一个信号,表示对这束流失去兴趣。可以传入 reason 参数表示取消原因,这个原因将传回给调用方。
getIterator创建一个异步的 ReadableStream 迭代器并将流锁定于其上。一旦流被锁定,其他读取器将不能读取它,直到它被释放。
getReader创建一个读取器并将流锁定于其上。一旦流被锁定,其他读取器将不能读取它,直到它被释放。
pipeThrough提供将当前流管道输出到一个 transform 流或 writable/readable 流对的链式方法。
pipeTo将当前 ReadableStream 管道输出到给定的 WritableStream (en-US),并返回一个 promise,输出过程成功时返回 fulfilled,在发生错误时返回 rejected。
teetee 方法(tee本意是将高尔夫球放置在球座上)tees 了可读流,返回包含两个ReadableStream 实例分支的数组,每个元素接收了相同的传输数据。

看了一下ReadableStream的各种方法,水很深,暂时搁置一下,后面有时间再分析把。

File,Blob,ArrayBuffer小结

以上,我们了解了File,Blob,ArrayBuffer的一些基本概念和功能。

我们知道了File,Blob,ArrayBuffer都是二进制数据对象,

其中File是文件级别二进制对象,Blob和ArrayBuffer都是普通二进制对象

但是Blob相当于File的切片,即文件切片,是类文件二进制对象。File也可以看成一种特殊的Blob。

而Blob只有四个方法 slice,text,arrayBuffer,stream,File也继承了这四个方法,可以对文件对象进行操作。

但是Blob提供的操作都是宏观的,而ArrayBuffer可以提供更加微观的,基于字节的操作。

ArrayBuffer是一个字节数组,数组元素就是字节,相当于八位二进制数,ArrayBuffer本身无法直接对数组元素进行操作,需要转成TypeArray后才能进行字节元素的修改,另外需要注意的是TypeArray一旦初始化就无法改变自身字节数,即ArrayBuffer虽然可以间接的修改数组中的字节元素,但是无法增加和删除字节元素。

File,Blob,ArrayBuffer转换

三者之间有如下转换关系

文件的BASE64编码

当我们通过input[type=file].files[0]获取到用户选择的文件File对象时,前端一般需要将该File对象上传到服务器,我们知道form表单(enctype=multipart/form-data)可以上传File对象,XMLHttpRequest(Content-Type=multipart/form-data;boundary=随机值)也可以上传File对象,这两种方式都是将文件以二进制数据进行传输。那么有没有办法将文件以文本进行传输呢?

我们知道文件除了二进制编码外,还可以base64编码

BASE64是一种编码方式,通常用于把二进制数据编码为可写的字符形式的数据。这是一种可逆的编码方式。

那么如何实现将文件从二进制编码转为BASE64编码呢?这就要靠FileReader了

FileReader

FileReader - Web API 接口参考 | MDN (mozilla.org)")

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

FileReader对象可以读取File或Blob中的二进制数据,考虑到文件可能很大,所以读取操作是异步的。

FileReader对象有一个属性readyState,有三个取值

判断

回到题目,如果你真想检验一个人的水平。第一步先考察一下基本的编程基础,问几个基本的编程问题,可以和前端相关也可以无关。比如垃圾收集大致是怎么做的,setTimeout 大致做了什么(说会在另一个线程里执行回调的直接毙掉)。

第二步考察一下知识面,问问http、tcp的基本知识,dns是怎么工作的,或者常用框架的实现原理,看看候选人是不是除了自己的一亩三分地什么都不关心。

第三步考察hold业务逻辑的能力,从一个简单的注册页,或者查询页开始,先让说下代码的基本架构,然后需求、性能、可靠性、安全层层加码,看看能不能很快的反馈出解决方案。能对答如流的要么做过,要么对他来说这种复杂度的东西是小case。

前三步都没问题,基本上说明候选人已经还行了,但是行到什么程度,不知道。如果想找比较厉害的,就增加个第四步,亮点项目考察。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了,我整理一套前端面试题分享给大家,希望对即将去面试的小伙伴们有帮助!

页,或者查询页开始,先让说下代码的基本架构,然后需求、性能、可靠性、安全层层加码,看看能不能很快的反馈出解决方案。能对答如流的要么做过,要么对他来说这种复杂度的东西是小case。

前三步都没问题,基本上说明候选人已经还行了,但是行到什么程度,不知道。如果想找比较厉害的,就增加个第四步,亮点项目考察。

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

总的来说,面试官要是考察思路就会从你实际做过的项目入手,考察你实际编码能力,就会让你在电脑敲代码,看你用什么编辑器、插件、编码习惯等。所以我们在回答面试官问题时,有一个清晰的逻辑思路,清楚知道自己在和面试官说项目说技术时的话就好了,我整理一套前端面试题分享给大家,希望对即将去面试的小伙伴们有帮助!

[外链图片转存中…(img-oe29IEK8-1714770224301)]

[外链图片转存中…(img-U2aMHvJE-1714770224302)]

  • 25
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值