<input type="file">

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

试一试

文件输入的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

虽然最初仅在基于WebKit的浏览器中实现,但webkitdirectory也可用于Microsoft Edge以及Firefox 50及更高版本。但是,即使它具有相对广泛的支持,它仍然不是标准的,除非您别无选择,否则不应使用它。

唯一的文件类型说明符

唯一文件类型指定符是一个字符串,用于描述用户可以在类型为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属性返回,该属性是一个包含File对象列表的FileList对象。FileList的行为类似于数组,因此您可以检查其length属性以获取所选文件的数量。

每个File对象包含以下信息

name

文件的名称。

lastModified

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

lastModifiedDate 已弃用

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

size

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

type

文件的MIME类型

webkitRelativePath 非标准

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

注意:您可以在所有现代浏览器中设置和获取HTMLInputElement.files的值;这最近添加到Firefox中,在版本57中(请参阅Firefox错误1384030)。

限制可接受的文件类型

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

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

  • accept="image/png"accept=".png" - 接受PNG文件。
  • accept="image/png, image/jpeg"accept=".png, .jpg, .jpeg" - 接受PNG或JPEG文件。
  • accept="image/*" - 接受任何具有image/* MIME类型的文件。(许多移动设备在使用此选项时也允许用户使用摄像头拍摄照片。)
  • 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/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`;
  } else {
    return `${(number / 1e6).toFixed(1)} MB`;
  }
}

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

示例如下;试一试

技术摘要

表示所选文件路径的字符串。
事件 changeinputcancel
支持的常用属性 required
其他属性 acceptcapturemultiple
IDL属性 filesvalue
DOM接口

HTMLInputElement

方法 select()
隐式ARIA角色 没有对应的角色

规范

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

浏览器兼容性

BCD表格仅在浏览器中加载

另请参阅