前端上传与下载基础:Blob、File与ArrayBuffer详解

前言

上传与下载是每个前端必定会遇到的需求场景,上传我们常用封装好的UI组件,例如Antd的Upload组件,Element的el-upload组件,下载使用同事封装好的hook或者file-saver包。这些通常能对付大部分情况,但我们不能只会用,否则遇到特定需求或新增功能时,会非常的麻烦。

让我们来了解上传与下载相关的:Blob、File与ArrayBuffer

一、概述

Blob:专门用于支持文件操作的二进制对象

File:继承Blob,并额外包含文件的元数据,如文件名和修改日期

ArrayBuffer:通用二进制缓冲区,类似数组,但长度固定,不可以增加和删除

关系图:

二、Blob基本使用

1. 创建Blob对象

可以使用 new Blob() 构造函数来创建一个 Blob 对象

js 复制代码
new Blob(blobParts) 
new Blob(blobParts, options)

blobParts:一个可迭代对象,比如 Array,包含 ArrayBuffer、TypedArray、DataView、Blob、字符串或者任意这些元素的混合,这些元素将会被放入 Blob 中。

options:可选参数,表示媒体类型。媒体类型列表参照

js 复制代码
new Blob(["Hello World"], { type: "text/plain" })

从打印的结果来看,Blob对象有2个实例属性,4个实例方法

2. 实例属性

  1. Blob.size:Blob 对象中所包含数据的大小(字节)

  2. Blob.type:表明该 Blob 对象所包含数据的媒体类型

3. 实例方法

  1. Blob.arrayBuffer:返回一个 promise,将Blob转为ArrayBuffer对象

Blob是一个文件对象,缺乏对二进制数据的细节操作能力,比如要具体修改某一部分的二进制数据,则需先转换为ArrayBuffer。例如对于图像或音频文件,有时候需要直接操作像素值或音频样本,这时就需要将 Blob 转换成 ArrayBuffer,再进一步解析成更具体的格式

js 复制代码
const blob = new Blob(["Hello World"], { type: "text/plain" })

blob.arrayBuffer().then((buffer) => {
  console.log(buffer);
});
  1. Blob.slice:返回一个新的 Blob 对象,类似于数组的slice方法,可以提取指定范围内的数据。常用于大文件分段上传。
js 复制代码
const part1 = blob.slice(0, 5);
  1. Blob.stream:返回一个可读流(ReadableStream),用于逐步读取Blob内容。常用于大文件分段读取。
js 复制代码
async function processBlob(blob) {
  const reader = blob.stream().getReader();
  const decoder = new TextDecoder('utf-8');
  let done, value;

  while (!done) {
    ({ value, done } = await reader.read());

    if (value) {
      // 将 Uint8Array 转换为字符串
      console.log(decoder.decode(value, { stream: true }));
    }
  }

  console.log("完成读取");
}

// 创建一个包含文本的 Blob
const myBlob = new Blob(['Hello, world!'], { type: 'text/plain' });

// 处理 Blob
processBlob(myBlob);
  1. Blob.text:返回一个 Promise,解析为文本数据
js 复制代码
blob.text().then((text) => {
  console.log(text);
});

三、Blob对象获取方式

共有五种常用方式获取Blob对象

1. 使用构造函数创建

js 复制代码
new Blob(["Hello World"])

2. 使用 fetch 获取

js 复制代码
fetch("https://wen.caowj.top/static/wx.jpg")
.then((res) => res.blob())
.then((blob) => {
  console.log(blob);
});

3. 使用 XMLHttpRequest 获取

js 复制代码
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://wen.caowj.top/static/wx.jpg", true);
xhr.responseType = "blob";
xhr.onload = function () {
  if (xhr.status === 200) {
    console.log(xhr.response);
  }
};

xhr.onerror = function () {};
xhr.send();

4. 从 Canvas 中获取

canvas提供了canvas.toBlob() 方法将画布内容转换为 Blob 对象

js 复制代码
const canvas = document.getElementById('myCanvas');
canvas.toBlob(function(blob) {
  console.log(blob);
}, 'image/png');

5. 从文件输入中获取

从文件输入中获取File对象共有2种方式:1. 标签上所选择的文件;2. 拖拽中生成的DataTransfer对象。

每个 File 对象都是 Blob 的子类,Blob上的实例属性和方法在File上都可以使用。这里以input为例。

js 复制代码
<input type="file" id="fileInput">

<script>
document.getElementById('fileInput').addEventListener('change', function(event) {
  const files = event.target.files; 
  if (files.length > 0) {
    const file = files[0]; 
    console.log(file);
  }
});
</script>

四、Blob能够实现的功能

1. 图片显示

通过window.URL.createObjectURL(blob)生成Blob URL,赋给img.src属性

js 复制代码
<body>
<input type="file" id="inputId" />
<img id="img" style="width: 200px; height: 200px" />
<script>
  document.getElementById("inputId").addEventListener(
    "change",
    function (e) {
      var file = this.files[0];
      const img = document.getElementById("img");
      const url = window.URL.createObjectURL(file);
      img.src = url;
      img.onload = function () {
        // 释放一个之前通过调用 URL.createObjectURL创建的 URL 对象
        window.URL.revokeObjectURL(url);
      };
    },
    false
  );
</script>
</body>

Blob URL只是类似于浏览器内部的引用,实际的数据通常会保存在浏览器的内存中

2. 文件下载

通过window.URL.createObjectURL(blob)生成Blob URL,赋值给a.download属性。

js 复制代码
<script>
const blob = new Blob(["Hello World"]);
const url = URL.createObjectURL(blob); // 创建一个 Blob URL
const a = document.createElement("a");
a.href = url;
a.download = "Hello.txt";
a.click();
URL.revokeObjectURL(url); // 释放URL对象,避免内存泄漏
</script>

提示:download属性不兼容IE, 对IE可通过window.navigator.msSaveBlob方法

3. 文件上传

文件上传常见的使用场景就是更换用户头像,更换头像不涉及大文件上传,因此这里不展示切片上传,而是直接上传。大文件上传之后再另开一篇介绍。

js 复制代码
<body>
<input type="file" id="inputId" />
<img id="img" style="width: 200px; height: 200px" />
<script>
  document.getElementById("inputId").addEventListener(
    "change",
    function (e) {
      //获取blob对象
      const blob = this.files[0];
      const formData = new FormData();
      //将 File 对象传入到 formData 中上传,对应的属性为file
      formData.append("file", blob);
      fetch("http://127.0.0.1:9000/api/upload", {
        method: "POST",
        body: formData,
      }).then((response) => {
        console.log("File uploaded successfully");
      });
    },
    false
  );
</script>
</body>

后端部分可参考我的另一篇文章:Express文件上传

4. 本地读取文件

Blob除了通过window.URL.createObjectURL转为Blob URL对文件进行下载或图片显示,还可以借助FileReader转为其他数据类型:

  1. FileReader.readAsText(Blob):将Blob转化为文本字符串

  2. FileReader.readAsArrayBuffer(Blob): 将Blob转为ArrayBuffer格式数据

  3. FileReader.readAsDataURL(): 将Blob转化为Base64格式的Data URL

例:将Blob转为Base64格式的Data URL,显示图片

js 复制代码
<body>
<input type="file" id="inputId" />
<img id="img" style="width: 200px; height: 200px" />
<script>
  document.getElementById("inputId").addEventListener(
    "change",
    function (e) {
      var file = this.files[0];
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = function () {
        const url = reader.result;
        const img = document.getElementById("img");
        img.src = url;
      };
    },
    false
  );
</script>
</body>

五、ArrayBuffer

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

1. 与原生数组区别

(1)ArrayBuffer初始化后固定大小

(2)数组放在堆中,ArrayBuffer把数据放在栈中

(3)ArrayBuffer没有push、pop等数组的方法

(4)ArrayBuffer只能读不能写,写要借助TypeArray或者DataView

2. ArrayBuffer对象获取方式

1. 通过Blob直接转换

js 复制代码
const blob = new Blob(["Hello World"], { type: "text/plain" });
blob.arrayBuffer().then((buffer) => {
  console.log(buffer);
});

2. 通过FileReader.readAsArrayBuffer转换

js 复制代码
<body>
<input type="file" id="inputId" />
<script>
  document.getElementById("inputId").addEventListener(
    "change",
    function (e) {
      var file = this.files[0];
      const reader = new FileReader();
      reader.readAsArrayBuffer(file);
      reader.onload = function () {
        const result = reader.result;
        console.log(result)
      };
    },
    false
  );
</script>
</body>

3. 使用 fetch 获取

js 复制代码
fetch("https://wen.caowj.top/static/wx.jpg")
.then((res) => res.arrayBuffer())
.then((arrayBuffer) => {
  console.log(arrayBuffer);
});

4. 使用 XMLHttpRequest 获取

js 复制代码
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://wen.caowj.top/static/wx.jpg", true);
xhr.responseType = "arraybuffer";
xhr.onload = function () {
    console.log(xhr.response)
}
xhr.send();

3. TypeArray

TypedArray是一个描述底层二进制数据缓冲区的类数组视图,它是一个泛称,可具体到以下这些:Int8Array、Uint8Array、Uint8ClampedArray、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array 和 Float64Array。每种类型都有各自的使用场景。

这里以Uint8Array为例,Uint8Array 用于存储 8 位无符号整数。它只能表示从 0 到 255 的非负整数值。适合处理文本编码、图片像素等。

  1. 基本使用
js 复制代码
// 直接创建一个 TypedArray,会自动创建一个 ArrayBuffer
const uint8 = new Uint8Array(2);
uint8[0] = 255; // 最大值
uint8[1] = 0;   // 最小值
console.log(uint8); // 输出: Uint8Array [255, 0]
  1. 示例:对两段文本进行拼接,模拟分段下载的场景
js 复制代码
<script>
function getArrayBuffer(text) {
  const blob = new Blob([text], { type: "text/plain" });
  return blob.arrayBuffer();
}

//模拟网络请求1
const p1 = getArrayBuffer("Hello World");
//模拟网络请求2
const p2 = getArrayBuffer("jack");

Promise.all([p1, p2]).then((res) => {
  const [buffer1, buffer2] = res;

  const arr = new Uint8Array(buffer1.byteLength + buffer2.byteLength);

  //通过 set 方法将两个 ArrayBuffer 的内容复制到新数组中
  arr.set(new Uint8Array(buffer1), 0);
  arr.set(new Uint8Array(buffer2), buffer1.byteLength);

  const blob = new Blob([arr.buffer]);
  const url = URL.createObjectURL(blob);

  const a = document.createElement("a");
  a.href = url;
  a.download = "Hello.txt";
  a.click();
  URL.revokeObjectURL(url);
});
</script>

4. DataView

DataView 是另一个视图,相比于TypeArray手动挑选类型,它允许你在同一个缓冲区内混合使用多种数据类型

js 复制代码
<script>
// 创建一个 16 字节的 ArrayBuffer
const buffer = new ArrayBuffer(16);
const view = new DataView(buffer);
view.setInt8(2, 42);
console.log(view.getInt8(2));
// 输出: 42
</script>

总结

参考文章:

  1. 聊聊JS的二进制家族:Blob、ArrayBuffer和Buffer

  2. 前端速通Blob、File、FileReader、ArrayBuffer、Base64、URL.createObjectURL()

相关推荐
2401_878454531 小时前
Themeleaf复用功能
前端·学习
葡萄城技术团队3 小时前
基于前端技术的QR码API开发实战:从原理到部署
前端
八了个戒4 小时前
「数据可视化 D3系列」入门第三章:深入理解 Update-Enter-Exit 模式
开发语言·前端·javascript·数据可视化
noravinsc5 小时前
html页面打开后中文乱码
前端·html
小满zs5 小时前
React-router v7 第四章(路由传参)
前端·react.js
小陈同学呦5 小时前
聊聊双列瀑布流
前端·javascript·面试
键指江湖6 小时前
React 在组件间共享状态
前端·javascript·react.js
诸葛亮的芭蕉扇6 小时前
D3路网图技术文档
前端·javascript·vue.js·microsoft
小离a_a6 小时前
小程序css实现容器内 数据滚动 无缝衔接 点击暂停
前端·css·小程序
徐小夕7 小时前
花了2个月时间研究了市面上的4款开源表格组件,崩溃了,决定自己写一款
前端·javascript·react.js