<input type="file">

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

类型为 type="file"<input> 元素允许用户从其设备存储中选择一个或多个文件。选择后,这些文件可以使用表单提交上传到服务器,或者使用 JavaScript 代码和 文件 API 进行处理。

试一试

<label for="avatar">Choose a profile picture:</label>

<input type="file" id="avatar" name="avatar" accept="image/png, image/jpeg" />
label {
  display: block;
  font:
    1rem "Fira Sans",
    sans-serif;
}

input,
label {
  margin: 0.4rem 0;
}

文件输入的 value 属性包含一个字符串,该字符串表示所选文件的路径。如果尚未选择文件,则该值为空字符串("")。当用户选择多个文件时,value 表示他们选择的文件列表中的第一个文件。其他文件可以使用输入的 HTMLInputElement.files 属性来识别。

注意: 该值始终是文件名前缀为 C:\fakepath\ 的文件名称,而不是文件的真实路径。这是为了防止恶意软件猜测用户的文件结构。

附加属性

除了所有 <input> 元素共有的常见属性外,file 类型的输入还支持以下属性。

accept

accept 属性值是一个字符串,它定义了文件输入应接受的文件类型。此字符串是以逗号分隔的唯一文件类型说明符列表。由于给定的文件类型可能以多种方式识别,因此当您需要给定格式的文件时,提供一组完整的类型说明符会很有用。

例如,Microsoft Word 文件可以通过多种方式识别,因此接受 Word 文件的网站可以使用如下 <input>

html
<input
  type="file"
  id="docpicker"
  accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />

capture

capture 属性值是一个字符串,它指定用于捕获图像或视频数据的摄像头,如果 accept 属性指示输入应为这些类型之一。user 值表示应使用面向用户的摄像头和/或麦克风。environment 值指定应使用面向外部的摄像头和/或麦克风。如果此属性缺失,用户代理可以自行决定如何操作。如果请求的朝向模式不可用,用户代理可能会回退到其首选的默认模式。

注意: capture 以前是一个布尔属性,如果存在,则请求使用设备的媒体捕获设备(例如摄像头或麦克风)而不是请求文件输入。

multiple

当指定 multiple 布尔属性时,文件输入允许用户选择多个文件。

非标准属性

除了上面列出的属性外,某些浏览器还提供以下非标准属性。您应尽可能避免使用它们,因为这样做会限制您的代码在不实现它们的浏览器中运行的能力。

webkitdirectory

布尔属性 webkitdirectory(如果存在)指示文件选择器界面中仅应允许用户选择目录。有关其他详细信息和示例,请参见 HTMLInputElement.webkitdirectory

唯一文件类型说明符

唯一文件类型说明符是一个字符串,它描述了用户可以在类型为 file<input> 元素中选择的文件类型。每个唯一文件类型说明符可以采用以下形式之一:

  • 一个有效的、不区分大小写的文件名扩展名,以句点(“.”)字符开头。例如:.jpg.pdf.doc
  • 一个有效的 MIME 类型字符串,没有扩展名。
  • 字符串 audio/* 表示“任何音频文件”。
  • 字符串 video/* 表示“任何视频文件”。
  • 字符串 image/* 表示“任何图像文件”。

accept 属性接受一个包含一个或多个这些唯一文件类型说明符的字符串作为其值,用逗号分隔。例如,需要呈现为图像的内容(包括标准图像格式和 PDF 文件)的文件选择器可能如下所示:

html
<input type="file" accept="image/*,.pdf" />

使用文件输入

一个基本示例

html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">Choose file to upload</label>
    <input type="file" id="file" name="file" multiple />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

这会产生以下输出:

注意: 您也可以在 GitHub 上找到此示例 — 请参阅源代码,并查看实时运行

无论用户的设备或操作系统如何,文件输入都提供一个按钮,该按钮会打开一个文件选择器对话框,允许用户选择文件。

如上所示,包含 multiple 属性指定可以一次选择多个文件。用户可以通过其所选平台允许的任何方式(例如,按住 ShiftControl 然后单击)从文件选择器中选择多个文件。如果您只希望用户为每个 <input> 选择一个文件,请省略 multiple 属性。

获取所选文件的信息

所选文件由元素的 HTMLInputElement.files 属性返回,该属性是一个 FileList 对象,其中包含一个 File 对象列表。FileList 的行为类似于数组,因此您可以检查其 length 属性以获取所选文件的数量。

每个 File 对象包含以下信息:

name

文件的名称。

lastModified

一个数字,指定文件上次修改的日期和时间,以自 UNIX 纪元(1970 年 1 月 1 日午夜)以来的毫秒数表示。

lastModifiedDate 已弃用

一个 Date 对象,表示文件上次修改的日期和时间。此属性已弃用,不应使用。请改用 lastModified

size

文件大小(以字节为单位)。

type

文件的 MIME 类型

webkitRelativePath 非标准

一个字符串,指定文件相对于在目录选择器中选择的基目录(即设置了 webkitdirectory 属性的 file 选择器)的路径。此属性为非标准属性,应谨慎使用。

限制接受的文件类型

通常,您不希望用户能够选择任意类型的文件;相反,您通常希望他们选择特定类型或多种类型的文件。例如,如果您的文件输入允许用户上传个人资料图片,您可能希望他们选择网络兼容的图像格式,例如 JPEGPNG

可以使用 accept 属性指定可接受的文件类型,该属性接受允许的文件扩展名或 MIME 类型的逗号分隔列表。一些示例:

  • accept="image/png"accept=".png" — 接受 PNG 文件。
  • accept="image/png, image/jpeg"accept=".png, .jpg, .jpeg" — 接受 PNG 或 JPEG 文件。
  • accept="image/*" — 接受任何 MIME 类型为 image/* 的文件。(许多移动设备在使用此属性时也允许用户使用摄像头拍照。)
  • accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document" — 接受任何像 MS Word 文档的文件。

我们来看一个更完整的例子:

html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="profile_pic">Choose file to upload</label>
    <input
      type="file"
      id="profile_pic"
      name="profile_pic"
      accept=".jpg, .jpeg, .png" />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

这会产生与上一个示例类似的输出:

注意: 您也可以在 GitHub 上找到此示例 — 请参阅源代码,并查看实时运行

它看起来可能相似,但如果您尝试使用此输入选择文件,您会看到文件选择器只允许您选择 accept 值中指定的文件类型(确切的界面因浏览器和操作系统而异)。

accept 属性不验证所选文件的类型;它为浏览器提供提示,以引导用户选择正确的文件类型。在大多数情况下,用户仍然可以在文件选择器中切换一个选项,使其能够覆盖此限制并选择他们想要的任何文件,然后选择不正确的文件类型。

因此,您应该确保 accept 属性由适当的服务器端验证支持。

检测取消

当用户未更改其选择,重新选择之前选择的文件时,会触发 cancel 事件。当文件选择器对话框通过“取消”按钮或 escape 键关闭或取消时,也会触发 cancel 事件。

例如,如果用户在未选择文件的情况下关闭弹出窗口,以下代码将记录到控制台:

js
const elem = document.createElement("input");
elem.type = "file";
elem.addEventListener("cancel", () => {
  console.log("Cancelled.");
});
elem.addEventListener("change", () => {
  if (elem.files.length === 1) {
    console.log("File selected: ", elem.files[0]);
  }
});
elem.click();

注意

  1. 您无法通过脚本设置文件选择器的值——执行以下操作无效:

    js
    const input = document.querySelector("input[type=file]");
    input.value = "foo";
    
  2. 当使用 <input type="file"> 选择文件时,出于显而易见的安全原因,实际的源文件路径不会显示在输入的 value 属性中。相反,会显示文件名,并附带 C:\fakepath\ 前缀。这个怪癖有一些历史原因,但它在所有现代浏览器中都受支持,事实上,它在规范中有所定义

示例

在此示例中,我们将展示一个稍微更高级的文件选择器,它利用了 HTMLInputElement.files 属性中可用的文件信息,并展示了一些巧妙的技巧。

注意: 您可以在 GitHub 上查看此示例的完整源代码 — file-example.html也可在此处查看实时运行)。我们不会解释 CSS;JavaScript 是主要焦点。

首先,我们来看一下 HTML:

html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="image_uploads">Choose images to upload (PNG, JPG)</label>
    <input
      type="file"
      id="image_uploads"
      name="image_uploads"
      accept=".jpg, .jpeg, .png"
      multiple />
  </div>
  <div class="preview">
    <p>No files currently selected for upload</p>
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>

这与我们之前看到的类似——没有什么特别需要评论的。

接下来,我们来讲解 JavaScript。

在脚本的前几行,我们获取了表单输入本身以及类名为 .preview<div> 元素的引用。接下来,我们隐藏了 <input> 元素 — 我们这样做是因为文件输入往往丑陋、难以样式化,并且在不同浏览器中的设计不一致。您可以通过单击其 <label> 来激活 input 元素,因此最好在视觉上隐藏 input 并将标签样式化为按钮,以便用户知道如果他们想要上传文件,可以与它进行交互。

js
const input = document.querySelector("input");
const preview = document.querySelector(".preview");

input.style.opacity = 0;

注意: 使用 opacity 来隐藏文件输入,而不是 visibility: hiddendisplay: none,因为辅助技术将后两种样式解释为文件输入不可交互。

接下来,我们向输入添加一个事件监听器,以监听其所选值的更改(在此例中,当文件被选中时)。事件监听器会调用我们自定义的 updateImageDisplay() 函数。

js
input.addEventListener("change", updateImageDisplay);

每当调用 updateImageDisplay() 函数时,我们都会:

  • 使用 while 循环清空预览 <div> 的先前内容。

  • 获取包含所有选定文件信息的 FileList 对象,并将其存储在一个名为 curFiles 的变量中。

  • 通过检查 curFiles.length 是否等于 0 来查看是否未选择任何文件。如果是,则在预览 <div> 中打印一条消息,说明尚未选择文件。

  • 如果选择文件,我们会循环遍历每个文件,将其信息打印到预览 <div> 中。这里需要注意的事项:

  • 我们使用自定义的 validFileType() 函数来检查文件是否为正确类型(例如,accept 属性中指定的图像类型)。

  • 如果是,我们:

    • 将其名称和文件大小打印到前一个 <div> 内的列表项中(从 file.namefile.size 获取)。自定义的 returnFileSize() 函数返回一个格式良好的大小版本,以字节/KB/MB 为单位(默认情况下,浏览器以绝对字节报告大小)。
    • 通过调用 URL.createObjectURL(file) 生成图像的缩略图预览。然后,通过创建一个新的 <img> 并将其 src 设置为缩略图,将图像也插入到列表项中。
  • 如果文件类型无效,我们会在列表项中显示一条消息,告知用户需要选择不同的文件类型。

js
function updateImageDisplay() {
  while (preview.firstChild) {
    preview.removeChild(preview.firstChild);
  }

  const curFiles = input.files;
  if (curFiles.length === 0) {
    const para = document.createElement("p");
    para.textContent = "No files currently selected for upload";
    preview.appendChild(para);
  } else {
    const list = document.createElement("ol");
    preview.appendChild(list);

    for (const file of curFiles) {
      const listItem = document.createElement("li");
      const para = document.createElement("p");
      if (validFileType(file)) {
        para.textContent = `File name ${file.name}, file size ${returnFileSize(
          file.size,
        )}.`;
        const image = document.createElement("img");
        image.src = URL.createObjectURL(file);
        image.alt = image.title = file.name;

        listItem.appendChild(image);
        listItem.appendChild(para);
      } else {
        para.textContent = `File name ${file.name}: Not a valid file type. Update your selection.`;
        listItem.appendChild(para);
      }

      list.appendChild(listItem);
    }
  }
}

自定义的 validFileType() 函数接受一个 File 对象作为参数,然后使用 Array.prototype.includes() 检查 fileTypes 中的任何值是否与文件的 type 属性匹配。如果找到匹配项,函数返回 true。如果未找到匹配项,则返回 false

js
// https://mdn.org.cn/en-US/docs/Web/Media/Guides/Formats/Image_types
const fileTypes = [
  "image/apng",
  "image/bmp",
  "image/gif",
  "image/jpeg",
  "image/pjpeg",
  "image/png",
  "image/svg+xml",
  "image/tiff",
  "image/webp",
  "image/x-icon",
];

function validFileType(file) {
  return fileTypes.includes(file.type);
}

returnFileSize() 函数接受一个数字(字节数,取自当前文件的 size 属性),并将其转换为格式良好的字节/KB/MB 大小。

js
function returnFileSize(number) {
  if (number < 1e3) {
    return `${number} bytes`;
  } else if (number >= 1e3 && number < 1e6) {
    return `${(number / 1e3).toFixed(1)} KB`;
  }
  return `${(number / 1e6).toFixed(1)} MB`;
}

注意: 这里的“KB”和“MB”单位使用的是 SI 前缀约定,即 1KB = 1000B,类似于 macOS。不同的系统以不同的方式表示文件大小——例如,Ubuntu 使用 IEC 前缀,其中 1KiB = 1024B,而 RAM 规范通常使用 SI 前缀来表示 2 的幂(1KB = 1024B)。因此,我们使用 1e31000)和 1e6100000)而不是 10241048576。在您的应用程序中,如果确切大小很重要,您应该清楚地向用户说明单位系统。

该示例看起来像这样;试玩一下:

技术摘要

一个表示所选文件路径的字符串。
事件 change, inputcancel
支持的常见属性 required
附加属性 accept, capture, multiple
IDL 属性 filesvalue
DOM 接口 HTMLInputElement
方法 select()
隐式 ARIA 角色 没有对应的角色

规范

规范
HTML
# file-upload-state-(type=file)

浏览器兼容性

另见