选择和互动用户本地设备上的文件是 Web 最常用的功能之一。它允许用户选择文件并将其上传到服务器,例如在分享照片或提交税务文件时。它还允许网站读取和操纵这些数据,而无需通过网络传输数据。本页将逐步介绍如何使用 JavaScript 与文件进行交互。
新版 File System Access API
File System Access API 提供了一种在用户本地系统上读取文件和目录以及向其中写入数据的方式。大多数基于 Chromium 的浏览器(例如 Chrome 和 Edge)都支持此功能。如需详细了解,请参阅 File System Access API。
由于 File System Access API 并非与所有浏览器都兼容,因此我们建议使用 browser-fs-access,这是一个辅助库,可在新 API 可用的情况下使用该 API,并在不可用的情况下回退到旧版方法。
以传统方式处理文件
本指南介绍了如何使用旧版 JavaScript 方法与文件进行交互。
选择文件
选择文件主要有两种方式:使用 HTML 输入元素和使用拖放区域。
HTML 输入元素
用户选择文件的最简单方法是使用 <input type="file">
元素,该元素在所有主流浏览器中均受支持。用户点击此元素后,可以使用操作系统内置的文件选择界面选择一个文件,如果包含 multiple
属性,则可以选择多个文件。当用户完成文件选择后,元素的 change
事件会触发。您可以从 event.target.files
(即 FileList
对象)访问文件列表。FileList
中的每个项都是一个 File
对象。
<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
const fileSelector = document.getElementById('file-selector');
fileSelector.addEventListener('change', (event) => {
const fileList = event.target.files;
console.log(fileList);
});
</script>
以下示例允许用户使用其操作系统的内置文件选择界面选择多个文件,然后将每个所选文件记录到控制台。
限制用户可以选择的文件类型
在某些情况下,您可能需要限制用户可以选择的文件类型。例如,图片编辑应用应仅接受图片,而不接受文本文件。如需设置文件类型限制,请向输入元素添加 accept
属性,以指定接受哪些文件类型:
<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">
自定义拖放
在某些浏览器中,<input type="file">
元素也是放置目标,允许用户将文件拖放到您的应用中。不过,此放置目标较小,可能难以使用。不过,在使用 <input type="file">
元素提供核心功能后,您可以提供一个大型的自定义拖放界面。
选择空投区
放置表面取决于应用的设计。您可能只希望窗口的一部分作为放置表面,但也可以使用整个窗口。

图片压缩应用 Squoosh 可让用户将图片拖动到窗口中的任意位置,并点击选择图片来调用 <input type="file">
元素。无论您选择哪个区域作为放置区,都应确保用户清楚知道可以将文件拖放到该区域。
定义放置区
如需将元素设为拖放区域,请为以下两个事件创建监听器:dragover
和 drop
。dragover
事件会更新浏览器界面,以直观地表明拖放操作正在创建文件副本。用户将文件拖放到界面上后,系统会触发 drop
事件。与输入元素一样,您可以从 event.dataTransfer.files
(即 FileList
对象)访问文件列表。FileList
中的每个项都是一个 File
对象。
const dropArea = document.getElementById('drop-area');
dropArea.addEventListener('dragover', (event) => {
event.stopPropagation();
event.preventDefault();
// Style the drag-and-drop as a "copy file" operation.
event.dataTransfer.dropEffect = 'copy';
});
dropArea.addEventListener('drop', (event) => {
event.stopPropagation();
event.preventDefault();
const fileList = event.dataTransfer.files;
console.log(fileList);
});
event.stopPropagation()
和 event.preventDefault()
会停止浏览器的默认行为,并让您的代码运行。如果没有这些设置,浏览器会离开您的网页,并打开用户拖放到浏览器窗口中的文件。
如需查看实时演示,请参阅自定义拖放。
目录呢?
遗憾的是,目前还没有使用 JavaScript 访问目录的好方法。
<input type="file">
元素上的 webkitdirectory
属性可让用户选择一个或多个目录。大多数主流浏览器都支持,但 Android 版 Firefox 和 iOS 版 Safari 除外。
如果启用了拖放功能,用户可能会尝试将目录拖到放置区。当放置事件触发时,它会包含目录的 File
对象,但不会提供对目录中任何文件的访问权限。
读取文件元数据
File
对象包含有关文件的元数据。大多数浏览器都会提供文件名、文件大小和 MIME 类型,不过根据平台的不同,不同的浏览器可能会提供不同或额外的信息。
function getMetadataForFileList(fileList) {
for (const file of fileList) {
// Not supported in Safari for iOS.
const name = file.name ? file.name : 'NOT SUPPORTED';
// Not supported in Firefox for Android or Opera for Android.
const type = file.type ? file.type : 'NOT SUPPORTED';
// Unknown cross-browser support.
const size = file.size ? file.size : 'NOT SUPPORTED';
console.log({file, name, type, size});
}
}
您可以在 input-type-file
演示中查看此功能的实际效果。
读取文件内容
使用 FileReader
将 File
对象的内容读入内存。您可以告知 FileReader
将文件读取为数组缓冲区、数据网址或文本:
function readImage(file) {
// Check if the file is an image.
if (file.type && !file.type.startsWith('image/')) {
console.log('File is not an image.', file.type, file);
return;
}
const reader = new FileReader();
reader.addEventListener('load', (event) => {
img.src = event.target.result;
});
reader.readAsDataURL(file);
}
此示例读取用户提供的 File
,然后将其转换为数据网址,并使用该数据网址在 img
元素中显示图片。如需了解如何验证用户是否已选择图片文件,请参阅 read-image-file
演示。
监控文件读取的进度
读取大型文件时,提供一些用户体验来告知用户读取进度会很有帮助。为此,请使用 FileReader
提供的 progress
事件。progress
事件具有两个属性:loaded
(读取的量)和 total
(要读取的量)。
function readFile(file) {
const reader = new FileReader();
reader.addEventListener('load', (event) => {
const result = event.target.result;
// Do something with result
});
reader.addEventListener('progress', (event) => {
if (event.loaded && event.total) {
const percent = (event.loaded / event.total) * 100;
console.log(`Progress: ${Math.round(percent)}`);
}
});
reader.readAsDataURL(file);
}
主打图片由 Vincent Botta 拍摄,选自 Unsplash