Skip to content
学习 衡量 博客 Case studies 关于
本页内容
  • 现代的文件系统访问 API
  • 选择文件
    • HTML 输入元素
    • 自定义拖放
    • 如何处理目录?
  • 读取文件元数据
  • 读取文件内容
    • 监控文件读取进度
  • Home
  • All articles

用 JavaScript 读取文件

如何选择文件、读取文件元数据和内容以及监控读取进度。

Jun 18, 2010 — 更新日期 Mar 29, 2021
Available in: Español, 日本語, 한국어, Português, Русский, English
Kayce Basques
Kayce Basques
TwitterGitHubGlitchHomepage
Pete LePage
Pete LePage
TwitterGitHubGlitchHomepage
Thomas Steiner
Thomas Steiner
TwitterGitHubGlitchHomepage
本页内容
  • 现代的文件系统访问 API
  • 选择文件
    • HTML 输入元素
    • 自定义拖放
    • 如何处理目录?
  • 读取文件元数据
  • 读取文件内容
    • 监控文件读取进度

最常用的网络功能之一就是能够选择用户本地设备上的文件并与之进行交互。该功能允许用户选择文件并将其上传到服务器,例如上传照片或提交税务文件等。但是,这项功能也使网站能够在无需通过网络传输数据的情况下对文件进行读取和操作。

现代的文件系统访问 API #

文件系统访问 API 提供了一种简单的方法来对用户本地系统中的文件和目录进行读取和写入。在大多数 Chromium 衍生浏览器(如 Chrome 或 Edge)中都可以使用该 API。如需了解更多信息,请参阅文件系统访问 API 一文。

由于文件系统访问 API 尚不与所有浏览器兼容,因而您可以了解一下 browser-fs-access,这是一个辅助库,会在新 API 可用时使用该 API,而在不兼容时退而采用传统方法。

处理文件,经典方式 #

本指南会向您展示如何:

  • 选择文件
    • 使用 HTML 输入元素
    • 使用拖放区
  • 读取文件元数据
  • 读取文件内容

选择文件 #

HTML 输入元素 #

要想使用户能够选择文件,最简单方法是使用各大主流浏览器都支持的<input type="file">元素。单击该元素后,用户就能够使用他们操作系统的内置文件选择用户界面来选择单个或多个文件(如果元素中包含multiple属性)。用户选择了一个或多个文件后,将会触发change事件。您可以从event.target.files访问文件列表,该列表是一个FileList对象。FileList中的每一项都是一个File对象。

<!-- `multiple` 属性使用户能够选择多个文件。 -->
<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>
请检查window.showOpenFilePicker()方法对于您的用例是否是一个可行的替代方案,因为该方法还为您提供了一个文件句柄,所以除了读取文件之外,您还可以回写文件。您可以使用这种方法进行 polyfill。

该示例让用户使用他们操作系统的内置文件选择用户界面选择多个文件,然后将每个选定的文件记录到控制台。

限制用户可以选择的文件类型 #

在某些情况下,您可能希望限制用户可以选择的文件类型。例如,一个图像编辑应用程序应该只接受图像,而不是文本文件。为此,您可以向输入元素中添加一个accept属性来指定可接受的文件类型。

<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">

自定义拖放 #

在某些浏览器中,<input type="file">元素也是一个放置目标,能够让用户将文件拖放到您的应用程序中。但是放置目标很小,可能较难使用。其实,在您使用<input type="file">元素提供了核心功能后,就可以提供一个较大的自定义拖放表面。

请检查DataTransferItem.getAsFileSystemHandle()方法对于您的用例是否是一个可行的替代方案,因为该方法还为您提供了一个文件句柄,所以除了读取文件之外,您还可以回写文件。

选择您的放置区 #

您的放置表面将取决于您的应用程序设计。您可能只希望将一部分窗口作为放置表面,也可能希望整个窗口都是放置表面。

Squoosh(图像压缩网络应用程序)的截图。
Squoosh 将整个窗口设置成了放置区。

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();
// 将拖放作为"复制文件"的操作。
event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
event.stopPropagation();
event.preventDefault();
const fileList = event.dataTransfer.files;
console.log(fileList);
});

event.stopPropagation()和event.preventDefault()会阻止浏览器的默认行为,并转而允许您的代码运行。如果没有这两条,浏览器就会离开您的页面,并打开用户放入浏览器窗口的文件。

请查看自定义拖放中的现场演示。

如何处理目录? #

很可惜,我们至今都还没找到访问目录的好方法。

<input type="file">元素上的webkitdirectory属性允许用户选择一个或多个目录。一些基于 Chromium 的浏览器支持这项功能,且桌面端 Safari 可能也支持该功能,但浏览器兼容性报告存在冲突。

请检查window.showDirectoryPicker()方法对于您的用例是否是一个可行的替代方案,因为该方法还为您提供了一个目录句柄,所以除了读取目录之外,您还可以回写目录。您可以使用这种方法进行 polyfill。

如果启用了拖放功能,用户可能会尝试将目录拖入放置区中。放置事件被触发时将包含该目录的一个File对象,但将无法访问目录中的任何文件。

读取文件元数据 #

File对象包含关于文件的许多元数据属性。大多数浏览器会提供文件名、文件大小和 MIME 类型,但因平台而异,不同浏览器可能会提供不同信息或附加信息。

function getMetadataForFileList(fileList) {
for (const file of fileList) {
// iOS 版 Safari 浏览器不支持。
const name = file.name ? file.name : 'NOT SUPPORTED';
// 安卓版 Firefox 浏览器及安卓版 Opera 浏览器不支持。
const type = file.type ? file.type : 'NOT SUPPORTED';
// 跨浏览器支持情况未知。
const size = file.size ? file.size : 'NOT SUPPORTED';
console.log({file, name, type, size});
}
}

您可以在input-type-file Glitch 演示中看到实际运用。

读取文件内容 #

请使用FileReader来读取文件,该函数使您能够将File对象的内容读入内存。您可以指示FileReader将文件读取为数组缓冲区、数据 URL 或文本。

function readImage(file) {
// 检查文件是否为图像。
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,然后将其转换为数据 URL,并使用该数据 URL 在img元素中显示图像。请查看read-image-file Glitch,了解如何验证用户是否选择了图像文件。

监控文件读取进度 #

在读取大文件时,提供指示读取进度的用户体验可能会有所帮助。要实现这一点,请使用FileReader提供的progress事件。progress事件提供了两项属性,即loaded(已读取量)和total(需读取总量)。

function readFile(file) {
const reader = new FileReader();
reader.addEventListener('load', (event) => {
const result = event.target.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)

存储
Last updated: Mar 29, 2021 — Improve article
Return to all articles
分享
订阅

Contribute

  • 提交错误
  • 查看源代码

相关内容

  • developer.chrome.com
  • Chrome 动态
  • 网站开发基础
  • 案例研究
  • 播客
  • 节目

连接

  • Twitter
  • YouTube
  • Google Developers
  • Chrome
  • Firebase
  • Google Cloud Platform
  • 所有产品
  • 条款和隐私权
  • 社区准则

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies.