一图看懂ArrayBuffer、Blob、File、FileReader、MediaSource之间的关系

592 阅读5分钟

Blob关系小.png

ArrayBuffer

ArrayBuffer 表示一个通用的、固定长度的原始二进制数据缓冲区。
ArrayBuffer 不能直接操作,而是通过类型化数组视图(TypedArray)(比如 Uint8ArrayFloat32Array 等)或者DataView 来操作。

应用:ArrayBuffer 经常与 Fetch API、WebSocket 等网络技术一起使用,用于处理二进制数据的接收和发送;可以用来处理文件、图像、音频、视频等二进制数据。

1. 创建实例

const buffer = new ArrayBuffer(16); // 创建一个16字节的ArrayBuffer

2. 操作实例

1)类型化数组视图(TypedArray)
const view = new Uint8Array(buffer); // 0 到 255  一个 8 位无符号整型数组 
view[0] = 255; // 设置第一个字节的值为255
2)DataView
const view = new DataView(buffer, 0);
view.setUint8(0, 255); // 在视图开始的指定字节偏移处存储一个无符号 8 位整数,设置第一个字节的值为255

3. 使用举例

刚好上一篇 前端实时播放摄像头RTSP流(H.265)解决方案 有用到

1)转换为字符串

我们不能直接将 ArrayBuffer 转换为字符串,但可以通过类型化数组视图等来实现:

private onWebSocketMessage = (buffer: ArrayBuffer) => { // ws 推过来的视频流
    const unit = new Uint8Array(buffer) 
    let unitCharCodeStr = '' 
    unit.forEach((u) => { 
        unitCharCodeStr += String.fromCharCode(u) 
    })
    console.log(unitCharCodeStr);
}
2)实时播放流媒体文件

SourceBuffer.appendBuffer:将来自 ArrayBufferTypedArray 或 DataView 对象的媒体片段数据附加到 SourceBuffer
官方示例:

const assetURL = "frag_bunny.mp4";
const mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';

if ("MediaSource" in window && MediaSource.isTypeSupported(mimeCodec)) {
  const mediaSource = new MediaSource();
  //console.log(mediaSource.readyState); // closed
  mediaSource.addEventListener("sourceopen", sourceOpen);
  video.src = URL.createObjectURL(mediaSource);
} else {
  console.error("Unsupported MIME type or codec: ", mimeCodec);
}

function sourceOpen() {
  //console.log(this.readyState); // open
  const mediaSource = this;
  const sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
  fetchAB(assetURL, function (buf) {
    sourceBuffer.addEventListener("updateend", () => {
      mediaSource.endOfStream();
      video.play();
      //console.log(mediaSource.readyState); // ended
    });
    sourceBuffer.appendBuffer(buf);
  });
}

Blob、File

Blob 对象表示一个不可变、原始数据的类文件对象。它的数据可以按文本或二进制的格式进行读取,也可以转换成 ReadableStream 来用于数据操作。
File 是 Blob 的一个子类,表示文件系统中的文件,扩展了 name 、 lastModified 等属性,可以在 Blob 可以使用的任何上下文中使用。

应用:通常用于处理文件或二进制数据流,比如从网络下载文件或用户上传文件到服务器。

1. 创建实例

new Blob(blobParts, options):
blobParts:一个可迭代对象,比如 Array,包含 ArrayBufferTypedArrayDataViewBlob、字符串或者任意这些元素的混合,这些元素将会被放入 Blob中。
options:一个可以指定以下任意属性的对象,type(将会被存储到 blob 中的数据的 MIME 类型),endings(如果数据是文本,那么如何解释其中的换行符\n

new File(blobParts, fileName, options):
fileName:表示文件名或文件路径的字符串。
options:除typeendings外,还可指定lastModified(一个数字,表示 Unix 时间纪元与文件上次修改时间之间的毫秒数,默认值为 Date.now()返回值)

// Blob
const blobParts = ['<q id="a"><span id="b">hey!</span></q>']; // 一个包含单个字符串的数组
const blob = new Blob(blobParts, { type: "text/html" }); // 得到 blob

// File
const file = new File(["foo"], "foo.txt", {
  type: "text/plain",
});

在实际应用中,File 对象通常从用户使用<input>元素选择文件返回的FileList对象中检索,或者从拖放操作返回的DataTransfer对象中检索

2. 操作实例

File可以在 Blob 可以使用的任何上下文中使用

1)实例方法:可读取或切片数据

image.png

2)通过FileReader 读取数据
const reader = new FileReader();
reader.onload = function(event) {
  console.log(event.target.result); // 读取到的数据
};
reader.readAsText(blob);

FileReader 相关API介绍见后文

3)利用URL.createObjectURL(blob)将 Blob 转换为 URL
const url = URL.createObjectURL(blob);
// 将 URL 用于图像
const img = document.createElement('img');
img.src = url;
img.onload = () => {
    URL.revokeObjectURL(img.src);
};
document.body.appendChild(img);

URL.createObjectURL(object):
该静态方法对 object 对象创建一个临时的、唯一的 URL,它指向一个内存中的文件对象,而不是实际的文件系统中的文件。这个 URL 可以用于 imgvideoaudio 等元素的 src 属性。
其中 object 指用于创建 URL 的 FileBlob 或 MediaSource对象。

3. 使用举例

<input type="file" id="fileInput">
1)本地图片预览(File
const fileInput = document.getElementById('fileInput');

fileInput.addEventListener('change', (event) => {
  const files = event.target.files;
  if (files.length > 0) {
    const file = files[0];
    const img = document.createElement('img');
    // 方案1: 使用 URL.createObjectURL()
    // start
    const url = URL.createObjectURL(file);
    img.src = url; 
    img.onload = () => { 
        URL.revokeObjectURL(img.src); 
    }; 
    // end
    
    // 方案2: 使用 FileReader()
    // start
    const reader = new FileReader();
    reader.onload = function(e) {
        img.src = e.target.result;
    };
    reader.readAsDataURL(file); // base64
    // end
    
    document.body.appendChild(img);
  }
});
2)本地文件上传(File
const fileInput = document.getElementById('fileInput');

fileInput.addEventListener('change', (event) => {
  const files = event.target.files;
  if (files.length > 0) {
    const file = files[0];
    const formData = new FormData();
    formData.append('file', file);
    fetch('xxxxx', {
      method: 'POST',
      body: formData
    })
    .then(response => response.json())
  }
});

3)文件下载(Blob
/* 从服务器下载 */
fetch('xxxxxxx')
    .then(response => response.blob())
    .then(blob => {
        var url = URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = 'filename.txt';
        document.body.appendChild(a);
        a.click();
        a.remove();
        URL.revokeObjectURL(url);
    })
/* 前端生成文件内容并下载 */
const testJson = {
    config: {"aa": 11, "bb": 22},
    inputDatas: [{"int1":23}, {"int2":34}]
};
const jsonBlob = new Blob([JSON.stringify(testJson, null, 2)], {type: 'application/json'});

const url = window.URL.createObjectURL(jsonBlob);
let a = window.document.createElement('a');
a.href = url;
a.download = 'test.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);

FileReader

FileReader 允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
文件对象可以从 <input> 选择文件后的 FileList 中获取,或者从拖放的 DataTransfer 中获取。

应用:提供一种方便的方式在客户端处理文件,无需服务器的介入。使得可以实现如图片预览、文件内容编辑等客户端功能。

1. 创建实例

const reader = new FileReader();

2. 操作实例

当读取操作完成时,readyState 属性变为 DONE,并触发 loadend 事件

image.png

image.png

3. 使用举例

使用 FileReader 读取文本文件

document.getElementById('fileInput').addEventListener('change', function(event) {
  const file = event.target.files[0];
  if (file) {
    const reader = new FileReader();
    
    reader.onload = function(e) {
      console.log(e.target.result); // 打印文件内容
    };
    reader.onerror = function(e) {
      console.error('读取文件时出错:', e.target.error);
    };
    
    reader.readAsText(file); // 以文本形式读取文件
  }
});

其他API使用,例如 readAsDataURL 已在上文提及,就不再赘述

MediaSource

MediaSourceMedia Source Extensions API(媒体源扩展 API(MSE))中用于表示媒体资源 HTMLMediaElement 对象的接口。
使用 MSE,媒体串流能够通过 JavaScript 创建,并且能通过使用 <audio> 和 <video> 元素进行播放。

由于这块内容涉及面广,且本文主要介绍 ArrayBufferMediaSource 的关系,就不在此对 MSE 做展开介绍。 感兴趣的也可以看下上一篇 前端实时播放摄像头RTSP流(H.265)解决方案,有涉及到相关介绍和应用。

总结

总体关系可大致为下面两条线:

Blob简洁版.png