《图解 + 实战》File、Blob、TypeArray、DataView

前言

最近一段时间做过很多关于文件及音视频相关的工作,故此根据个人实践结合资料查询学习,总结了关于文件、二进制相关的内容和大家分享交流,如果文章有不对的地方,恳请指正,✅通过本文,你将了解或者加深对以下概念的认知以及其应用场景:

  • File
  • Blob
  • ArrayBuffer
  • TypeArray
  • DataView
  • Buffer
  • FileReader
  • URL.createObjectURL

以及如何实现 ?

  1. 如何预览图片?
  2. 如何截取视频首帧(如果不借助第三方oss能力呢)?

1. Blob

Blob是对大数据块的不透明引用或者句柄。名字源于SQL数据库,表示"二进制大数据"(Binary Large Object)。在JavaScript中Blob通常表示二进制数据,但是不一定是大量数据。Blob是不透明的,我们可以对它执行的操作只有获取它的大小,MIME类型和将他切割成更小的Blob。------《JavaScript权威指南》

1.1 主要用法

1.1.1 构造函数使用

第一个参数是一个包含实际数据的数组(二进制数组也行),第二个参数是数据的类型(MIME type

1.1.2 现有实例使用

其构造器Blob原型有size和type属性分别表示blob包含数据的大小和数据类型,其原型上方法有text(返回promise)和slice, 其中slice(start, end),返回start和end为切割blob的开始和结束字节点,返回值为blob

1.2 应用场景

  1. 大文件上传(阿里云)时,会对file继承的blob的slice属性,对文件进行切割分片上传
  2. 可以通过blob结合FileReader来把其换成其他需要的数据比如Arraybuffer
  3. 可以暂存arrayBuffer数据,并通过URL.createObjectUrl(blob)通过同步的方式将其转成url以提供引用

2. File

File继承Blob,并基于用户的操作系统拓展了blob,使用户可以通过浏览器安全的访问系统的

3. 获取File

3.1 从操作系统获取

3.1.1 input元素返回的filelist

3.1.2 拖放操作获取File

3.2 从网络中获取

从网络中获取文件(资源图片、视频),常用方式有两种

  1. 资源是存在cdn上面,通过url去访问获取资源
  2. 通过推流的方式获取资源(图片)
方式 特点 应用场景
cdn 不经常变动的资源 前端的脚本、用户信息头像、商品图、素材库图等存储资源
二进制流 经常变动、实时生成 二维码(小程序体验版),ai画像等

获取的二进制流可以通过blob转成url或者通过bota编码转成base64(见下文)

4. 操作文件

4.1 上传大文件对文件(file/blob)进行分割

4.2 图片裁切 🔧中将blob同步转为base64(dataul)

当然也可以通过FileReader异步的读取blob/file来获取base64

4.3 Base64 转为 blob

4.4 处理音视频首帧

上述流程简单说明:

  1. 通过拖拽或者选择的方式选取我们要上传的视频 file
  2. 为了获取视频首帧)通过createObjectUrl(file)获取到视频url
  3. 通过videoElement引用url,并插入dom
  4. 通过loadeddata事件回调🪝,canvas创建画布,通过toDataURL获取到视频首帧截图
  5. 然后再将其转成blob(4.3 Base64 转为 blob
  6. 调用oss方法上传首帧文件

5. ArrayBuffer/ DataView / TypeArray

为什么要使用它

  1. 原生的Array内部为了实现灵活性,牺牲了一部分性能,采用了哈希表的数据结构(详情
  2. buffer通过分配连续的内存,访问较原生array哈希方式遍历链表索引 更加高效

5.1 实例

canvas typeArray

Array vs Buffer

名称 特点 应用场景
Array 不限制类型、可能是稀疏的、不定长度 大部分场景及json
ArrayBuffer 实际存储数据的地方(内存) XHR、File API、Canvas等等各种地方,读取了一大串字节流
TypedArray 单个长度和类型固定、字节对齐 音视频、canvas、录音,处理网络中接收到的二进制数据,webwork中的大量数据处理
DataView 每个元素长度和类型不固定,更灵活,性能相对较差 构建二进制文件或格式话文件
js 复制代码

5.2 ArrayBuffer

以上述代码为例子:

  • line1: 申请了一个大小为8byte的内存大小区域,其地址是arrBuff
  • line2、line3: 在arrBuff对象上添加0a属性,并且为其赋值

注意:ArrayBuffer创建后,不能通过其引用直接操作(读写)它的元素,给其赋值,其实是在给其添加静态属性值,下面我们会证明为什么

这里后端拿到的response.data是arraybuffer并不能直接操作,而是通过typeArray进行操作的

5.3 TypeArray

注意!全局并没有TypeArray,其是作为Int8ArrayUint16Array等子类的统称,只是这些之类的原型,,所以只能通过 Object.getPrototypeOf(Int8Array) 及类似方式访问

5.3.1 举个🌰子

图片标记点按照数字依次为:

  1. 二进制类型数组中的元素,默认填充0
  2. 类型数组实际引用的buffer,只读
  3. 每个字节的地址
  4. 不同类型type array
  5. 进制类型:dec(Decimal System) 十进制,hex 十六进制, oct 八进制
  6. 不同进制下对应的buffer存的数值

5.3.2 无符号类二进制数组

其形式为Uint +8^n +Array ,这里拿Uint8Array举例: 对于无符号8比特(1byte)的类型的

由上图得出Uint8Array有以下几点特点:

  1. 单个元素的可存值范围 [0, 256)
  2. 正向溢出,一直减去256,直至得到的值在[0, 256)区间的数字,作为其最终值
  3. 负向溢出,一直256,直至得到的值在[0, 256)区间的数字,作为其最终值
  4. 溢出的值,不会加到下一个元素

5.3.3 有符号类二进制数组

上述代码稍作改动,有符号8比特(1byte)的Int8Array类型的

由上图得出Int8Array有以下几点特点:

  1. 单个元素的可存值范围 [-128, 127)
  2. 正向溢出,一直减去256,直至得到的值在[-128, 127)区间的数字,作为其最终值
  3. 负向溢出,一直256,直至得到的值在[-128, 127)区间的数字,作为其最终值
  4. 溢出的值,不会加到下一个元素

5.3.4 主要用法及应用场景

  1. 使用arrayBuffer实例作为参数,提供操作arrayBuffer方法
  2. 创建一个特定类型的typearray
js 复制代码
new TypedArray()
new TypedArray(length)
new TypedArray(typedArray)
new TypedArray(object)

new TypedArray(buffer)
new TypedArray(buffer, byteOffset)
new TypedArray(buffer, byteOffset, length)

同一类型,如Int8ArrayUint8Array分为有符号和无符号,取值范围不同,其中取值范围表示,每个类数组(集合)的单个元素的取值范围

关于typeArray属性:

  1. length:上述代码构造函数中的3、6表示创建类型化数组长度(length)是3、6,即包含3、6个元素,用0填充
  2. bytelength, 数组的长度大小(字节),其为 BYTES_PER_ELEMENT * length
  3. BYTES_PER_ELEMENT: 类型化数组的每个元素的空间大小(字节)

注意在构建typeArray实例时

  1. 要注意传入的arrayBuffer的字节大小要和类型数组元素对齐,即buffer大小是自元素大小的整数倍,这样才能被类型元素平均分配
  2. 同理,偏移量offeset(字节),也要是BYTES_PER_ELEMENT倍数
    关于字节对齐: 下面是vue-cropper图片菜切开源项目,当传入到Uint16Array的字节大小不为2的整数倍时就会报错如图所示错误,所以需要对传入buffer的len做偶数适配

5.4 DataView

DataView 视图是一个可以从二进制 ArrayBuffer 对象中读写多种数值类型的底层接口 --- Mdn

其中关于setInt8^n和对应的getInt8^,拿 view.setInt16(7, 120)view.getInt16(7) 举例🌰, 调用过程是这样的:

  1. 通过var buff10 = new ArrayBuffer(10)偷到了ArrayBuffer老巢的地址,于是上门去找ArrayBuffer
  2. 到了之后,从前面偏移(空出来)7个字节开始,隔出来一个2byte的内存空间专门来存放有符号数据 120
  3. view.getInt16(7)从前面偏移(空出来)7个字节开始,获取一个2byte的内存空间专门内存的值

上述代码,内存操作示意图:

通过上述代码,我们可知:

  1. TypeArray和DataView都可以操作ArrayBuffer,但是前者更加灵活,每个元素长度和类型都可以不一致
  2. DataView 不会有对齐问题

##6. 总结

使用类型化数组的关键是性能和内存。它们最常用于特殊场景,但是当您只需要存储数值(或 utf-8 字符串、加密向量等)时,在普通情况下使用它们并没有什么问题。它们速度快且内存占用少

参考

  1. Arraybuffer、Blob、File、Buffer详解、作用以及相互转化
  2. 我妈都看得懂的 Buffer 基础
  3. Where to use ArrayBuffer vs typed array in JavaScript?
  4. javascript ArrayBuffer, what's it for?
  5. 怎么理解 JavaScript 中的 ArrayBuffer?
  6. 前端内存优化的探索与实践
  7. V8 ---DataView vs. TypedArray peak performance
  8. 阮一峰ArrayBuffer
  9. 二进制的原码、反码、补码
  10. What are Blobs used for in JavaScript?
相关推荐
莹雨潇潇5 分钟前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr13 分钟前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho1 小时前
【TypeScript】知识点梳理(三)
前端·typescript
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_029】3.5 给 D3 条形图加注图表标签(上)
开发语言·前端·javascript·信息可视化·数据可视化·d3.js
太阳花ˉ2 小时前
html+css+js实现step进度条效果
javascript·css·html
小白学习日记3 小时前
【复习】HTML常用标签<table>
前端·html
john_hjy3 小时前
11. 异步编程
运维·服务器·javascript
风清扬_jd3 小时前
Chromium 中JavaScript Fetch API接口c++代码实现(二)
javascript·c++·chrome
丁总学Java3 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js