Web 开发中 Blob 与 FileAPI 使用简述

本文节选自 Awesome CheatSheet/DOM CheatSheet,主要是对 DOM 操作中常见的 Blob、File API 相关概念进行简要描述。

Web 开发中 Blob 与 FileAPI 使用简述

Blob 是 JavaScript 中的对象,表示不可变的类文件对象,里面可以存储大量的二进制编码格式的数据。Blob 对象的创建方式与其他并无区别,构造函数可接受数据序列与类型描述两个参数:

const debug = { hello: 'world' };
let blob = new Blob([JSON.stringify(debug, null, 2)], {
  type: 'application/json'
});
// Blob(22) {size: 22, type: "application/json"}

// 也可以转化为类 URL 格式
const url = URL.createObjectURL(blob);
// "blob:https://developer.mozilla.org/88c5b6de-3735-4e02-8937-a16cc3b0e852"

// 设置自定义的样式类
blob = new Blob(['body { background-color: yellow; }'], {
  type: 'text/css'
});

link = document.createElement('link');
link.rel = 'stylesheet';
//createObjectURL returns a blob URL as a string.
link.href = URL.createObjectURL(blob);

其他的类型转化为 Blob 对象可以参考 covertToBlob.js,将 Base64 编码的字符串或者 DataUrl 转化为 Blob 对象。Blob 包括了 size 与 type,以及常用的用于截取的 slice 方法等属性。Blob 对象能够添加到表单中,作为上传数据使用:

const content = '<a id="a"><b id="b">hey!</b></a>'; // the body of the new file...
const blob = new Blob([content], { type: 'text/xml' });

formData.append('webmasterfile', blob);

slice 方法会返回一个新的 Blob 对象,包含了源 Blob 对象中指定范围内的数据。其实就是对这个 blob 中的数据进行切割,我们在对文件进行分片上传的时候需要使用到这个方法,即把一个需要上传的文件进行切割,然后分别进行上传到服务器:

const BYTES_PER_CHUNK = 1024 * 1024; // 每个文件切片大小定为1MB .
const blob = document.getElementById('file').files[0];
const slices = Math.ceil(blob.size / BYTES_PER_CHUNK);
const blobs = [];
Array.from({ length: slices }).forEach(function(item, index) {
  blobs.push(blob.slice(index, index + 1));
});

这里我们使用的 blob 对象实际上是 HTML5 中的 File 对象;HTML5 File API 允许我们对本地文件进行读取、上传等操作,主要包含三个对象:File,FileList 与用于读取数据的 FileReader。File 对象就是 Blob 的分支,或者说子集,表示包含某些元数据的单一文件对象;FileList 即是文件对象的列表。FileReader 能够用于从 Blob 对象中读取数据,包含了一系列读取文件的方法与事件回调,其基本用法如下:

const reader = new FileReader();
reader.addEventListener('loadend', function() {
  // reader.result 包含了 Typed Array 格式的 Blob 内容
});
reader.readAsArrayBuffer(blob);

blob = new Blob(['This is my blob content'], { type: 'text/plain' });
read.readAsText(bolb); // 读取为文本

// reader.readAsArrayBuffer   //将读取结果封装成 ArrayBuffer ,如果想使用一般需要转换成 Int8Array 或 DataView
// reader.readAsBinaryString  // 在IE浏览器中不支持改方法
// reader.readAsTex // 该方法有两个参数,其中第二个参数是文本的编码方式,默认值为 UTF-8
// reader.readAsDataURL  // 读取结果为DataURL
// reader.readyState // 上传中的状态

在图片上传中,我们常常需要获取到本地图片的预览,参考 antd/Upload 中的处理:

// 将文件读取为 DataURL
const previewFile = (file: File, callback: Function) => {
  const reader = new FileReader();
  reader.onloadend = () => callback(reader.result);
  reader.readAsDataURL(file);
};

// 设置文件的 DataUrl
previewFile(file.originFileObj, (previewDataUrl: string) => {
  file.thumbUrl = previewDataUrl;
});

// JSX
<img src={file.thumbUrl || file.url} alt={file.name} />;

另一个常用的场景就是获取剪贴板中的图片,并将其预览展示,可以参考 coding-snippets/image-paste:

const cbd = e.clipboardData;
const fr = new FileReader();

for (let i = 0; i < cbd.items.length; i++) {
  const item = cbd.items[i];

  if (item.kind == 'file') {
    const blob = item.getAsFile();
    if (blob.size === 0) {
      return;
    }

    previewFile(blob);
  }
}

标准的 Web 标准中提供了 FileReader 对象进行读取操作,不过 Chrome 中提供了 FileWriter 对象,允许我们在浏览器沙盒中创建文件,其基于 requestFileSystem 方法:

// 仅可用于 Chrome 浏览器中
window.requestFileSystem =
  window.requestFileSystem || window.webkitRequestFileSystem;

window.requestFileSystem(type, size, successCallback, opt_errorCallback);

简单的文件创建与写入如下所示:

function onInitFs(fs) {
  fs.root.getFile(
    'log.txt',
    { create: true },
    function(fileEntry) {
      // Create a FileWriter object for our FileEntry (log.txt).
      fileEntry.createWriter(function(fileWriter) {
        fileWriter.onwriteend = function(e) {
          console.log('Write completed.');
        };

        fileWriter.onerror = function(e) {
          console.log('Write failed: ' + e.toString());
        };

        // Create a new Blob and write it to log.txt.
        var blob = new Blob(['Lorem Ipsum'], { type: 'text/plain' });

        fileWriter.write(blob);
      }, errorHandler);
    },
    errorHandler
  );
}

window.requestFileSystem(window.TEMPORARY, 1024 * 1024, onInitFs, errorHandler);

某熊的全栈之路
知识,应该在它该在的地方。一个热爱代码,热爱新技术的程序熊。

爱代码 爱生活 希望成为全栈整合师

22.4k 声望
8.5k 粉丝
0 条评论
推荐阅读
2022-某熊的技术 & 产品成长之路:重启的第二曲线
在过去的 2022 年,伴随着经济寒冬感觉技术也进入了静默期,除了 AI 领域之外少了许多令人激动的技术突破、发现。在过去十年里因为各种新技术、新框架的出现、发展而诞生的各种技术造轮子、布道及各种新 KPI 的机...

王下邀月熊_Chevalier2阅读 1k评论 1

封面图
CSS transition 小技巧!如何保留 hover 的状态?
欢迎关注我的公众号:前端侦探通常情况下,hover 是无法保存状态的。鼠标移入触发额外样式,一旦移出就还原了 {代码...} 这就意味着,如果需要保留hover的状态,可能就不得不借助JS了,比如下面是某某书院的首页...

XboxYan30阅读 4k评论 2

封面图
由小见大!不规则造型按钮解决方案
今天,有个群友在群里提问,使用 CSS 能否实现下述这个图形:emmm,中间这个酷似三次贝塞尔曲线的造型,使用 CSS 不太好实现。我的建议是切图实现,然而群友要求一定要用 CSS 实现。虽然麻烦,但是这个图形勉强也...

chokcoco17阅读 1.3k

封面图
CSS 如何让auto height完美支持过渡动画?
欢迎关注我的公众号:前端侦探众所周知,高度在设置成auto关键词时是不会触发transition过渡动画的,下面是伪代码 {代码...} 效果如下如果希望展开时有过渡动画,例如这样通常是借助 JS 动态去获取元素的高度(还...

XboxYan17阅读 1.3k评论 2

封面图
vh 存在问题?试试动态视口单位之 dvh、svh、lvh
大部分同学都知道,在 CSS 世界中,有 vw、vh、vmax、vmin 这几个与视口 Viewport 相关的单位。正常而言:1vw 等于1/100的视口宽度 (Viewport Width)1vh 等于1/100的视口高度 (Viewport Height)vmin — vmin ...

chokcoco13阅读 1.4k评论 2

CSS 奇思妙想之酷炫倒影
在 CSS 中,倒影是一种比较常见的效果。今天,我们就将尝试,使用 CSS 完成各类不同的倒影效果,话不多说,直接进入主题。实现倒影的两种方式首先,快速过一下在 CSS 中,实现倒影的 2 种方式。使用 -webkit-box-...

chokcoco14阅读 1.9k评论 1

那些不用js也能实现的效果
本文首发于公众号:GitWeb,欢迎关注,接收首发推文本文列举几个不需要使用js也能实现的效果一、页面回到顶部回到顶部是页面开发中很常见的一个功能,一般的做法是对回到顶部组件做一个监听,当用户点击的时候,...

阿山10阅读 650评论 4

封面图

爱代码 爱生活 希望成为全栈整合师

22.4k 声望
8.5k 粉丝
宣传栏