File API 与 Blob API

 Web 应用程序的一个主要的痛点是无法操作用户计算机上的文件。 2000 年之前, 处理文件的唯一方式是把 < input type = "file" > 放到一个表单里, 仅此而已。 File API 与 Blob API 是为了让 Web 开发者能以安全的方式访问客户端机器上的文件, 从而更好地与这些文件交互而设计的。

一、File 类型

    File API 仍然以表单中的文件输入字段为基础, 但是增加了直接访问文件信息的能力。 HTML5 在DOM 上为文件输入元素添加了 files 集合。 当用户在文件字段中选择一个或多个文件时, 这个 files集合中会包含一组 File 对象, 表示被选中的文件。 每个 File 对象都有一些只读属性。

 name: 本地系统中的文件名。

 size: 以字节计的文件大小。

 type: 包含文件 MIME 类型的字符串。

 lastModifiedDate: 表示文件最后修改时间的字符串。 这个属性只有 Chome 实现了。

  例如, 通过监听 change 事件然后遍历 files 集合可以取得每个选中文件的信息:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>

<body>
	<input type="file" multiple id="files-list">
	<script>
		let filesList = document.getElementById("files-list");
		filesList.addEventListener("change", (event) => {
			let files = event.target.files,
				i = 0,
				len = files.length;
			while (i < len) {
				const f = files[i];
				console.log(`${f.name} (${f.type}, ${f.size} bytes)`);
				i++;
			}
		});
	</script>
</body>


</html>

  File API 还提供了 FileReader 类型, 让我们可以实际从文件中读取数据。

二、 FileReader 类型

FileReader类型表示一种异步文件读取机制。 可以把FileReader 想象成类似于XMLHttpRequest,只不过是用于从文件系统读取文件, 而不是从服务器读取数据。 FileReader 类型提供了几个读取文件数据的方法。

readAsText(file, encoding) :从文件中读取纯文本内容并保存在 result 属性中。第二个
参数表示编码,是可选的。
readAsDataURL(file) :读取文件并将内容的数据 URI 保存在 result 属性中。
readAsBinaryString(file) :读取文件并将每个字符的二进制数据保存在 result 属性中。
readAsArrayBuffer(file) :读取文件并将文件内容以 ArrayBuffer 形式保存在 result 属性。
这些读取数据的方法为处理文件数据提供了极大的灵活性。例如,为了向用户显示图片,可以将图
片读取为数据 URI ,而为了解析文件内容,可以将文件读取为文本。

因为这些读取方法是异步的, 所以每个 FileReader 会发布几个事件, 其中 3 个最有用的事件是progress、 error 和 load, 分别表示还有更多数据、 发生了错误和读取完成。

    progress 事件每 50 毫秒就会触发一次, 其与 XHR 的 progress 事件具有相同的信息: lengthComputable、 loaded 和 total。 此外, 在 progress 事件中可以读取 FileReader 的 result属性, 即使其中尚未包含全部数据。

    error 事件会在由于某种原因无法读取文件时触发。 触发 error 事件时, FileReader 的 error属性会包含错误信息。 这个属性是一个对象, 只包含一个属性: code。 这个错误码的值可能是 1( 未找到文件)、 2( 安全错误)、 3( 读取被中断)、 4( 文件不可读) 或 5( 编码错误)。

    load 事件会在文件成功加载后触发。 如果 error 事件被触发, 则不会再触发 load 事件。 下面的

    例子演示了所有这 3 个事件:

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<style>
		body {
			font-family: Arial, sans-serif;
			text-align: center;
			margin: 50px;
		}

		#output {
			margin-top: 20px;
			border: 1px solid #ccc;
			padding: 20px;
			min-height: 100px;
			background-color: #f9f9f9;
		}

		#progress {
			margin-top: 10px;
			font-size: 14px;
		}
	</style>
</head>

<body>
	<input type="file" multiple id="files-list">
	<div id="progress">Progress: 0/0</div>
	<div id="output"></div>
	<script>
		let filesList = document.getElementById("files-list");
		filesList.addEventListener("change", (event) => {
			let info = "",
				output = document.getElementById("output"),
				progress = document.getElementById("progress"),
				files = event.target.files,
				type = "default",
				reader = new FileReader();
			if (/image/.test(files[0].type)) {
				reader.readAsDataURL(files[0]);
				type = "image";
			} else {
				reader.readAsText(files[0]);
				type = "text";
			}
			reader.onerror = function () {
				output.innerHTML = "Could not read file, error code is " +
					reader.error.code;
			};

			reader.onprogress = function (event) {
				if (event.lengthComputable) {
					progress.innerHTML = `${event.loaded}/${event.total}`;
				}
			};
			reader.onload = function () {
				let html = "";
				switch (type) {
					case "image":
						html = `<img src="${reader.result}">`;
						break;
					case "text":
						html = reader.result;
						break;
				}
				output.innerHTML = html;
			};
		});
	</script>
</body>


</html>

以上代码从表单字段中读取一个文件, 并将其内容显示在了网页上。 如果文件的 MIME 类型表示它是一个图片, 那么就将其读取后保存为数据 URI, 在 load 事件触发时将数据 URI 作为图片插入页面中。如果文件不是图片, 则读取后将其保存为文本并原样输出到网页上。

progress 事件用于跟踪和显示读取文件的进度, 而 error 事件用于监控错误。

  如果想提前结束文件读取, 则可以在过程中调用 abort() 方法, 从而触发 abort 事件。 在 load、

    error 和 abort 事件触发后, 还会触发 loadend 事件。 loadend 事件表示在上述 3 种情况下, 所有读 取操作都已经结束。 readAsText() 和 readAsDataURL() 方法已经得到了所有主流浏览器支持。

三、FileReaderSync 类型

  顾名思义, FileReaderSync 类型就是 FileReader 的同步版本。 这个类型拥有与 FileReader

  相同的方法, 只有在整个文件都加载到内存之后才会继续执行。 FileReaderSync 只在工作线程中可用,因为如果读取整个文件耗时太长则会影响全局。

    假设通过 postMessage() 向工作线程发送了一个 File 对象。 以下代码会让工作线程同步将文件

  读取到内存中, 然后将文件的数据 URL 发回来:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Web Worker 文件处理示例</title>
</head>

<body>
	<input type="file" id="fileInput" />
	<div id="result"></div>
	<script>
		const fileInput = document.getElementById('fileInput');
		const resultDiv = document.getElementById('result');
		fileInput.addEventListener('change', function (e) {
			const file = e.target.files[0];
			if (file) {
				const worker = new Worker('worker.js');
				worker.onmessage = function (event) {
					const dataUrl = event.data;
					if (dataUrl.startsWith('data:application/pdf')) {
						const pdfIframe = document.createElement('iframe');
						pdfIframe.src = dataUrl;
						pdfIframe.width = '100%';
						pdfIframe.height = '600px';
						resultDiv.innerHTML = '';
						resultDiv.appendChild(pdfIframe);
					} else {
						resultDiv.innerHTML = `
        <p>读取结果: \${dataUrl}</p>`;
					}
				};
				worker.postMessage(file);
			}
		});
	</script>
</body>
</html>

// worker.js 
onmessage = (messageEvent) => {
	const syncReader = new FileReaderSync();
	console.log(syncReader); // FileReaderSync {} 

	// 读取文件时阻塞工作线程
	const result = syncReader.readAsDataURL(messageEvent.data);

	// PDF 文件的示例响应
	console.log(result); // data:application/pdf;base64,JVBERi0xLjQK...

	// 把 URL 发回去
	postMessage(result);
};

四、Blob 与部分读取

  某些情况下, 可能需要读取部分文件而不是整个文件。 为此, File 对象提供了一个名为 slice()

  的方法。 slice() 方法接收两个参数: 起始字节和要读取的字节数。 这个方法返回一个 Blob 的实例, 而 Blob 实际上是 File 的超类。

    blob 表示二进制大对象( binary larget object), 是 JavaScript 对不可修改二进制数据的封装类型。 包含字符串的数组、 ArrayBuffers、 ArrayBufferViews, 甚至其他 Blob 都可以用来创建 blob。 Blob构造函数可以接收一个 options 参数, 并在其中指定 MIME 类型:

	<script>
		console.log(new Blob(['foo']));
		// Blob {size: 3, type: ""} 
		console.log(new Blob(['{"a": "b"}'], {
			type: 'application/json'
		}));
		// {size: 10, type: "application/json"} 
		console.log(new Blob(['<p>Foo</p>', '<p>Bar</p>'], {
			type: 'text/html'
		}));
        // {size: 20, type: "text/html"}
	</script>

 Blob 对象有一个 size 属性和一个 type 属性, 还有一个 slice() 方法用于进一步切分数据。 另外也可以使用 FileReader 从 Blob 中读取数据。 下面的例子只会读取文件的前 32 字节:

 

<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>读取文件前32字节示例</title>
</head>

<body>
  <input type="file" id="files-list" />
  <div id="output"></div>
  <div id="progress"></div>

  <script>
    // 模拟的blobSlice函数
    function blobSlice(blob, start, end) {
      if (blob.slice) {
        return blob.slice(start, end);
      } else if (blob.mozSlice) {
        return blob.mozSlice(start, end);
      } else if (blob.webkitSlice) {
        return blob.webkitSlice(start, end);
      }
      return null;
    }

    let filesList = document.getElementById("files-list");
    filesList.addEventListener("change", (event) => {
      let info = "",
        output = document.getElementById("output"),
        progress = document.getElementById("progress"),
        files = event.target.files,
        reader = new FileReader(),
        blob = blobSlice(files[0], 0, 32);

      if (blob) {
        reader.readAsText(blob);
        reader.onerror = function () {
          output.innerHTML = "Could not read file, error code is " +
            reader.error.code;
        };

        reader.onload = function () {
          output.innerHTML = reader.result;
        };
      } else {
        console.log("Your browser doesn't support slice().");
      }
    });
  </script>
</body>

</html>

五、对象 URL Blob

对象 URL 有时候也称作 Blob URL ,是指引用存储在 File Blob 中数据的 URL 。对象 URL 的优
点是不用把文件内容读取到 JavaScript 也可以使用文件。只要在适当位置提供对象 URL 即可。要创建对 象 URL ,可以使用 window.URL.createObjectURL() 方法并传入 File Blob 对象。这个函数返回 的值是一个指向内存中地址的字符串。因为这个字符串是 URL ,所以可以在 DOM 中直接使用。例如, 以下代码使用对象 URL 在页面中显示了一张图片:
<!DOCTYPE html>
<html lang="zh-CN">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>文件选择并显示图片示例</title>
</head>

<body>
	<!-- 文件选择输入框 -->
	<input type="file" id="files-list">
	<!-- 用于显示输出结果 -->
	<div id="output"></div>
	<!-- 用于显示进度,虽然此处未实际使用进度功能 -->
	<div id="progress"></div>

	<script>
		// 获取文件选择输入框元素
		let filesList = document.getElementById("files-list");
		// 为文件选择输入框添加 change 事件监听器
		filesList.addEventListener("change", (event) => {
			let info = "";
			// 获取用于显示输出结果的元素
			let output = document.getElementById("output");
			// 获取用于显示进度的元素
			let progress = document.getElementById("progress");
			// 获取用户选择的文件列表
			let files = event.target.files;
			// 创建一个 FileReader 实例,虽然此处未使用它来读取文件
			let reader = new FileReader();
			// 为用户选择的第一个文件创建一个对象 URL
			let url = window.URL.createObjectURL(files[0]);

			if (url) {
				// 判断文件类型是否为图片
				if (/image/.test(files[0].type)) {
					// 如果是图片,将图片显示在页面上
					output.innerHTML = `<img src="${url}">`;
				} else {
					// 如果不是图片,显示提示信息
					output.innerHTML = "Not an image.";
				}
			} else {
				// 如果浏览器不支持创建对象 URL,显示提示信息
				output.innerHTML = "Your browser doesn't support object URLs.";
			}
		});
	</script>
</body>

</html>
如果把对象 URL 直接放到 <img> 标签,就不需要把数据先读到 JavaScript 中了。 <img> 标签可以直 接从相应的内存位置把数据读取到页面上。
使用完数据之后,最好能释放与之关联的内存。只要对象 URL 在使用中,就不能释放内存。如果
想表明不再使用某个对象 URL ,则可以把它传给 window.URL.revokeObjectURL() 。页面卸载时,
所有对象 URL 占用的内存都会被释放。不过,最好在不使用时就立即释放内存,以便尽可能保持页面 占用最少资源。

六、 读取拖放文件

组合使用 HTML5 拖放 API File API 可以创建读取文件信息的有趣功能。在页面上创建放置目标
后,可以从桌面上把文件拖动并放到放置目标。这样会像拖放图片或链接一样触发 drop 事件。被放置 的文件可以通过事件的 event.dataTransfer.files 属性读到,这个属性保存着一组 File 对象,就 像文本输入字段一样。
下面的例子会把拖放到页面放置目标上的文件信息打印出来:
<!DOCTYPE html>
<html lang="zh-CN">

<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>文件拖放示例</title>
	<style>
		#droptarget {
			width: 300px;
			height: 200px;
			border: 2px dashed #ccc;
			display: flex;
			justify-content: center;
			align-items: center;
			text-align: center;
			margin-bottom: 20px;
		}

		#output {
			min-height: 100px;
		}
	</style>
</head>

<body>
	<!-- 拖放目标区域 -->
	<div id="droptarget">将文件拖放到这里</div>
	<!-- 用于显示拖放文件的信息 -->
	<div id="output"></div>

	<script>
		// 获取拖放目标区域元素
		let droptarget = document.getElementById("droptarget");

		function handleEvent(event) {
			let info = "";
			// 获取用于显示文件信息的元素
			let output = document.getElementById("output");
			let files, i, len;
			// 阻止事件的默认行为
			event.preventDefault();

			if (event.type === "drop") {
				// 获取拖放的文件列表
				files = event.dataTransfer.files;
				i = 0;
				len = files.length;
				while (i < len) {
					// 拼接文件信息
					info += `${files[i].name} (${files[i].type}, ${files[i].size} bytes)<br>`;
					i++;
				}
				// 将文件信息显示在页面上
				output.innerHTML = info;
			}
		}

		// 为拖放目标区域添加 dragenter、dragover 和 drop 事件监听器
		droptarget.addEventListener("dragenter", handleEvent);
		droptarget.addEventListener("dragover", handleEvent);
		droptarget.addEventListener("drop", handleEvent);
	</script>
</body>

</html>
与拖放的例子一样,必须取消 dragenter dragover drop 的默认行为。在 drop 事件处理程序中,可以通过 event.dataTransfer.files 读到文件,此时可以获取文件的相关信息。

File API 和 Blob API 是浏览器提供的两个重要的 Web API,它们都与文件处理和二进制数据处理密切相关,但功能和应用场景有所不同。下面详细解释这两个 API 的功能及其常见的相关方法。

1. File API

File API 主要用于处理用户在浏览器中选择的文件。它提供了一些功能,使得开发者能够访问、读取和操作用户的文件(例如上传的文件)。

常见的接口和方法
  • File:代表用户选择的文件,是 Blob 对象的子类,表示文件数据的一个实例。

    • 属性
      • name: 文件的名称。
      • lastModified: 文件的最后修改时间戳。
      • size: 文件的大小,单位为字节。
      • type: 文件的 MIME 类型。
  • FileReader:用于异步读取文件内容。

    • 方法

      • readAsText(): 将文件读取为文本(字符串)。
      • readAsDataURL(): 将文件读取为 Data URL(通常用于图像文件)。
      • readAsArrayBuffer(): 将文件读取为原始二进制数据。
      • readAsBinaryString(): 将文件读取为二进制字符串(较少使用)。
    • 事件

      • onload: 文件成功读取后触发。
      • onerror: 读取过程中出错时触发。
      • onloadend: 读取结束后触发,无论成功或失败。
  • FileList:代表多个文件的集合,通常由 <input type="file"> 元素的 files 属性返回。

  • FormData:允许你构造一个用于提交的表单数据,包括文件,适用于与服务器的文件上传操作。

应用场景
  • 文件上传:通过文件选择器(<input type="file">)和 FileReader API 实现文件上传和预览。
  • 图像预览:选择图片后通过 FileReader 读取为 Data URL,实现文件预览功能。
  • 文件操作:获取文件的元数据(如大小、类型、修改日期等)并进行相应的处理。

2. Blob API

Blob(Binary Large Object)API 主要用于处理不可变的原始二进制数据。它可以存储文件、图像、音频、视频等多种格式的数据。在 File API 中,文件本质上是一个特殊的 Blob 对象。

常见的接口和方法
  • Blob:表示原始的二进制数据。Blob 对象的创建通常不依赖于用户的输入,而是由程序生成。

    • 构造函数Blob(array, options)
      • array: 一个包含多个数据片段的数组(例如:字符串、ArrayBuffer、Uint8Array 等)。
      • options: 一个可选的对象,包含 type 和 endings 属性。
        • type: 指定 MIME 类型(例如 image/jpeg)。
        • endings: 设置为 native 表示使用平台特定的换行符。
  • URL.createObjectURL():创建一个临时的 URL,用于引用一个 Blob 对象或 File 对象。通常用于将 Blob 数据转化为一个可以在 <img><video> 等标签中显示的 URL。

    • 例如:const url = URL.createObjectURL(blob);。然后可以将 url 赋值给 <img> 标签的 src 属性。
  • Blob.slice():通过指定起始和结束位置来创建 Blob 的切片。

    • slice(start, end, contentType):创建一个从 start 到 end 的新 Blob,并可以指定 MIME 类型。
应用场景
  • 数据传输:Blob 用于表示二进制数据,通常用于服务器和客户端之间的数据传输(例如:文件上传、下载、视频流等)。
  • 文件生成:Blob 可以生成大文件(如音频、视频等),并通过 URL.createObjectURL() 将它们提供给浏览器进行展示或下载。
  • 文件切割:Blob.slice() 用于大文件的分割上传,避免单次上传时数据过大。

File API 与 Blob API 的区别

  • File 是 Blob 的子类File 是继承自 Blob 的对象,除了拥有 Blob 的属性和方法外,它还包含了文件的名称、最后修改时间等元数据。简单来说,所有的 File 对象都是 Blob 对象,但并非所有的 Blob 对象都是文件。
  • File API 主要用于浏览器端处理用户上传的文件,包括读取、预览和上传文件等。
  • Blob API 更广泛地用于处理和生成二进制数据,不一定需要用户交互,更多应用在动态生成数据、流媒体等场景中。

总结

  • File API 主要用于处理文件操作,允许浏览器与用户的文件进行交互,特别是文件上传、读取等功能。
  • Blob API 提供了对二进制数据的处理能力,它支持数据的生成、切割和传输,适用于更广泛的二进制数据操作场景。

这两者可以一起使用,特别是在需要处理文件上传、生成文件下载或处理二进制数据时。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值