Skip to content

Blob ​

Blob 本质上也就是一个对象,可以用于表示一串数据。

我们可以创建一个 Blob 对象

js
const blob = new Blob(["this is a data","test"],{//第一个参数为一个数组,数组里放置数据,可以是图片,可以是文件,这里传一些字符串做测试
//第二个参数为配置对象
type:"text/plain"
});
console.log(blob);

![Pasted image 20240508205829](/notes-assets/05.WEBAPI/assets/Pasted image 20240508205829.png)

我们就就可以看到这个对象

这个对象的 type 和 size 属性,相信不用多说,无非就是大小和类型。

创建一个 blob 之后,如何获取 blob 中的数据呢?就要通过实例方法了,实例方法中有一个 arrayBuffer(),该方法会返回一个 Promise,数据为一个 ArrayBuffer 对象,我们可以通过类型化数组或者 dataView 去操作这个 ArrayBuffer 对象,这里不多介绍。

除了这个方法之外,还有三个方法,分别是 slice(),text(),stream()

首先介绍前面两个,首先是 slice(),我们知道 blob 本质上就是一个文件,所以既然是文件,是二进制的数据,我们当然可以给他截取一部分出来,slice 方法就可以把一个文件截取一部分出来,blob.slice(start,end,MIME),这个方法就会将 blob 对象的第 start 字节开始,截取到 end 字节结束,截取出来的新 blob 类型为第三个参数指定的类型。

接下来就是 text() 方法,这个方法返回一个 Promise 对象,Promise 对象中的数据就是 blob 中的内容,这个内容使用 UTF-8 进行编码。

js
const blob = new Blob(["this is a data", "test"], {
	type: "text/plain"
});
const firstBlob = blob.slice(0, 6, 'text/plain');
firstBlob.text().then(data => {
	console.log(data);//输出this i
})
js
const blob = new Blob(["this is a data", "test"], {
	type: "text/plain"
});
blob.text().then(data => {
	console.log(data);//this is a datatest
})

File ​

File 类实际上就是 Blob 类的子类,也就可以认为 File 本质上就是一个特殊的 Blob

我们要获得一个 File 对象有三种方法

  • 使用 File() 构造函数创建文件对象
  • file 类型的 input 元素中获取
  • dragAPI 中事件对象的 dataTransfer 中获取

首先介绍使用构造函数来创建,这种方式很少见,File 构造函数中需要至少两个参数,第一个参数为文件,可以是字符串,可以是 ArrayBuffer,可以是数组。第二个参数为文件名,第三个就是一些配置对象,配置对象里可以配置两个属性,分别是 type 和lastModified,第一个参数我相信不用说,就是 MIME 类型,第二个参数就是最后一次修改的事件,默认就是当前的毫秒级时间戳。由于这种方式比较少见,所以这里我懒得演示了。

我们更多是通过读取 file 类型的 input 元素以及 dragAPI 来获取文件对象,其实读者到这里应该明白,拖拽 API 最大的用处其实是数据传输,而数据传输最有用的莫过于上传和下载文件。

首先,file 类型的 input 元素可以用于上传文件,很显然,当我们选中文件之后,这个 input 元素中肯定有一个文件对象,现在的问题是如何通过 JS 获取这个文件对象。

html
<!DOCTYPE html>
<html lang="cmn-hans">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <input type="file" multiple name="" id="">
</body>
<script>
    const file = document.querySelector("input[type='file']");
    file.addEventListener("change", e => {
        console.log(e.target.files);//可以输出文件数组
    });
</script>

</html>

可以看到,在 file 类型的 input 元素中,有一个 files 属性,这个属性是一个伪数组,用于保存选中的多个 Files 对象。

![Pasted image 20240508213324](/notes-assets/05.WEBAPI/assets/Pasted image 20240508213324.png)

同样,在拖拽事件对象的 datatransfer 属性中其实也有一个 files 属性,可以获取到当前拖入文件的 File 对象。

html
<!DOCTYPE html>
<html lang="cmn-hans">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <input type="file" multiple name="" id="">
</body>
<script>
    document.addEventListener("dragover", e => {
        e.preventDefault();
    });
    document.addEventListener("drop", e => {
        e.preventDefault();
        console.log(e.dataTransfer.files);
    })
</script>
</html>

![recording 9](../04.CSS3/assets/recording 9.gif)

此时就拿到了拖入文件的 File 对象

文件对象中有一个webkitRelativePath 属性,改属性可以获取到文件的路径,但是这并不是一个标准属性。

FileReader ​

现在我们已经有了 File 对象了,实际上就已经在 JS 中拿到文件了,现在的问题是如何把这个文件读出来,要想把文件读出来,我们就需要一个文件读取器,也就是 FileReader 对象

我们可以直接使用构造函数生成一个 FileReader 对象

js
const fr = new FileReader();//这里就创建了一个FileReader对象

文件读取器可以帮助我们异步读取文件内容,这个异步是基于事件的,而非 Promise,这个注意。

这个对象上有很多方法和属性

  • error:返回读取文件时的错误信息
  • readyState:返回当前读取的状态,0 表示已经创建,但是还没开始读,1 表示正在读取,2 表示读取完毕
  • result:返回文件的内容,这个属性只有当 readyState 为 2 的时候,也就是读取完毕的时候才生效,这个文件的格式取决于你用什么格式去读取的文件。
  • readAsArrayBuffer ():以 ArrayBuffer 的格式读取文件
  • readAsText ():以字符串的形式读取文件
  • readAsDataUrl ():以 base 64 编码读取文件,一般读取图片用这个
  • abort ():终止读取,调用之后 readyState 变为 2

当我们调用这些方法之后,就会自动开始读取文件,那读取文件一定是一个异步操作,我们如何判断文件读取完成了呢?很简单,就使用事件来完成这个异步处理,文件读取器中有一些事件可供我们使用

  • abort:中止读取的时候触发
  • error:读取出现错误的时候触发
  • load:读取成功的时候触发
  • loadend:文件读取完成就触发,哪怕读取出现了问题也触发
  • loadstart:文件开始读取的时候触发
  • progress:文件读取过程中频繁触发,可以用来做进度条

这些事件的事件对象中添加了以下属性可供我们使用:

  • lengthComputable:布尔属性,表示此次读取的文件是否可计算
  • loaded:一个 64 位无符号整数,表示已经读取的数据量
  • total:一个 64 位无符号整数,表示数据总量

这里我们做一个简单的效果出来

html
<!DOCTYPE html>
<html lang="cmn-hans">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style>
        body {
            margin: 0;
        }
    </style>
</head>

<body>
    <img class="img" src="" alt="">
</body>
<script>
    /**
     * 初始化页面中的图像元素,并设置事件监听器以处理文件的拖放操作。
     * 该代码段不接受参数,也不返回任何值。
     */
    const img = document.querySelector(".img"); // 获取页面中类名为 img 的元素

    // 监听文档上的拖动事件,阻止默认行为以允许拖放
    document.addEventListener("dragover", e => {
        e.preventDefault();
    });

    // 监听文档上的放下事件,阻止默认行为并处理拖放的文件
    document.addEventListener("drop", e => {
        e.preventDefault();
        // 创建一个 FileReader 对象以读取拖放的文件
        const fr = new FileReader();
        
        // 当文件读取完成时,将图片数据赋值给 img 元素的 src 属性
        fr.addEventListener("load", e => {
            img.src = e.target.result;
        });
        
        // 读取拖放文件的数据,将其作为 Data URL 加载
        fr.readAsDataURL(e.dataTransfer.files[0]);
    })
</script>

</html>

以上代码实现了,当我们拖拽一张图片进入网页的时候,让 img 元素显示该图片。

这里只演示了读取图片,我们当然可以选择以 ArrayBuffer 的形式读取文件,或者以字符串的形式读取为文本,总之我们只要在 load 事件中拿到 FileReader 的 result 属性即可。

这里读者可能会联想到 Blob 中的 text 方法,这个方法可以返回一个 Promise,数据就是 Blob 对象中的字符串,以 UTF-8 编码。这个读取器的 readAsText 方法会更加强大,可以传入第二个参数,默认是 UTF-8,可以以其他的编码格式读取。

以笔记为镜,可以知得失