<input type="file">
<input>
元素使用 type="file"
允许用户从其设备存储中选择一个或多个文件。选择后,可以使用 表单提交 将文件上传到服务器,或者使用 JavaScript 代码和 文件 API 进行操作。
试一试
值
文件输入的value
属性包含一个字符串,表示所选文件(或文件)的路径。如果尚未选择任何文件,则值为一个空字符串 (""
)。当用户选择多个文件时,value
表示他们选择的文件列表中的第一个文件。可以使用输入的HTMLInputElement.files
属性来识别其他文件。
注意:该值始终是文件名,并在其前面添加了C:\fakepath\
,这不是文件的真实路径。这是为了防止恶意软件猜测用户的文件结构。
其他属性
除了所有<input>
元素共享的常用属性外,类型为file
的输入还支持以下属性。
accept
accept
属性值是一个字符串,用于定义文件输入应该接受的文件类型。此字符串是唯一文件类型指定符的逗号分隔列表。因为给定的文件类型可以通过多种方式识别,所以当您需要特定格式的文件时,提供一套完整的类型指定符非常有用。
例如,Microsoft Word文件可以通过多种方式识别,因此接受Word文件的站点可能会使用如下所示的<input>
<input
type="file"
id="docpicker"
accept=".doc,.docx,.xml,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
capture
multiple
当指定了multiple
布尔属性时,文件输入允许用户选择多个文件。
非标准属性
除了上面列出的属性之外,一些浏览器还提供了以下非标准属性。您应该尽量避免使用它们,因为这样做会限制您的代码在未实现它们的浏览器中运行的能力。
webkitdirectory
布尔webkitdirectory
属性(如果存在)表示在文件选择器界面中,只有目录可供用户选择。有关其他详细信息和示例,请参阅HTMLInputElement.webkitdirectory
。
虽然最初仅在基于WebKit的浏览器中实现,但webkitdirectory
也可用于Microsoft Edge以及Firefox 50及更高版本。但是,即使它具有相对广泛的支持,它仍然不是标准的,除非您别无选择,否则不应使用它。
唯一的文件类型说明符
唯一文件类型指定符是一个字符串,用于描述用户可以在类型为file
的<input>
元素中选择的某种文件类型。每个唯一文件类型指定符可以采用以下格式之一
- 以句点 (".") 字符开头的有效不区分大小写的文件名扩展名。例如:
.jpg
、.pdf
或.doc
。 - 有效的MIME类型字符串,不带扩展名。
- 字符串
audio/*
表示“任何音频文件”。 - 字符串
video/*
表示“任何视频文件”。 - 字符串
image/*
表示“任何图像文件”。
accept
属性采用一个字符串作为其值,该字符串包含一个或多个这些唯一文件类型指定符,并以逗号分隔。例如,需要可以显示为图像的内容(包括标准图像格式和PDF文件)的文件选择器可能如下所示
<input type="file" accept="image/*,.pdf" />
使用文件输入
一个基本示例
<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>
这将产生以下输出
无论用户的设备或操作系统如何,文件输入都提供一个按钮,该按钮会打开一个文件选择器对话框,允许用户选择文件。
如上所示,包含multiple
属性指定可以一次选择多个文件。用户可以通过其所选平台允许的任何方式(例如,按住Shift或Control然后单击)从文件选择器中选择多个文件。如果您只希望用户为每个<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)。
限制可接受的文件类型
通常,您不希望用户能够选择任意类型的文件;相反,您通常希望他们选择特定类型或类型的文件。例如,如果您的文件输入允许用户上传个人资料图片,您可能希望他们选择与网络兼容的图像格式,例如JPEG或PNG。
可以使用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文档的文件。
让我们看一个更完整的示例
<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>
这将生成与上一个示例类似的输出
它可能看起来很相似,但如果您尝试使用此输入选择文件,您会发现文件选择器只允许您选择accept
值中指定的文件类型(确切的界面在不同的浏览器和操作系统中有所不同)。
accept
属性不会验证所选文件的类型;它为浏览器提供提示,以引导用户选择正确类型的文件。用户仍然可以(在大多数情况下)切换文件选择器中的一个选项,使其能够覆盖此选项并选择他们想要的任何文件,然后选择不正确的文件类型。
因此,您应该确保accept
属性由适当的服务器端验证支持。
检测取消操作
当用户没有更改他们的选择,重新选择之前选择的的文件时,会触发cancel
事件。当文件选择器对话框通过“取消”按钮或escape键关闭或取消时,也会触发cancel
事件。
例如,以下代码将在用户关闭弹出窗口而未选择文件时记录到控制台
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();
注释
- 您不能从脚本设置文件选择器的值 - 执行以下操作无效js
const input = document.querySelector("input[type=file]"); input.value = "foo";
- 当使用
<input type="file">
选择文件时,源文件的真实路径不会显示在输入的value
属性中,原因很明显,是为了安全起见。而是显示文件名,并在其前面添加了C:\fakepath\
。此怪癖有一些历史原因,但在所有现代浏览器中都得到支持,事实上,它在规范中定义。
示例
在此示例中,我们将提供一个稍微高级一点的文件选择器,它利用了HTMLInputElement.files
属性中可用的文件信息,并展示了一些巧妙的技巧。
注意:您可以在GitHub上查看此示例的完整源代码 - file-example.html(也可以查看其运行情况)。我们不会解释CSS;JavaScript是主要焦点。
首先,让我们看一下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
并像按钮一样设置标签的样式,这样用户就会知道如果要上传文件,需要与之交互。
const input = document.querySelector("input");
const preview = document.querySelector(".preview");
input.style.opacity = 0;
注意:使用opacity
来隐藏文件输入,而不是visibility: hidden
或display: none
,因为辅助技术将后两种样式解释为文件输入不可交互。
接下来,我们向输入添加一个事件监听器
,以监听其所选值的更改(在本例中,当选择文件时)。事件监听器调用我们自定义的updateImageDisplay()
函数。
input.addEventListener("change", updateImageDisplay);
每当调用updateImageDisplay()
函数时,我们都会
- 使用
while
循环清空预览<div>
中的先前内容。 - 获取包含所有已选择文件信息的
FileList
对象,并将其存储在一个名为curFiles
的变量中。 - 检查是否未选择任何文件,方法是检查
curFiles.length
是否等于0。如果是,则在预览<div>
中打印一条消息,说明未选择任何文件。 - 如果已选择文件,则遍历每个文件,并将有关该文件的信息打印到预览
<div>
中。这里需要注意以下几点 - 我们使用自定义的
validFileType()
函数来检查文件是否为正确的类型(例如,accept
属性中指定的图像类型)。 - 如果是,我们
- 将文件名和文件大小打印到前一个
<div>
(从file.name
和file.size
获取)内的列表项中。自定义的returnFileSize()
函数以易于阅读的格式返回以字节/KB/MB表示的大小(默认情况下,浏览器以绝对字节报告大小)。 - 通过调用
URL.createObjectURL(file)
生成图像的缩略图预览。然后,通过创建一个新的<img>
并将它的src
设置为缩略图,将图像插入列表项中。
- 将文件名和文件大小打印到前一个
- 如果文件类型无效,则在列表项内显示一条消息,告诉用户需要选择不同的文件类型。
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
。
// 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格式的大小。
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)。因此,我们使用1e3
(1000
)和1e6
(100000
)而不是1024
和1048576
。在您的应用程序中,如果精确的大小很重要,则应向用户明确传达单位系统。
示例如下;试一试
技术摘要
规范
规范 |
---|
HTML标准 # file-upload-state-(type=file) |
浏览器兼容性
BCD表格仅在浏览器中加载
另请参阅
- 从Web应用程序中使用文件——包含许多其他与
<input type="file">
和文件API相关的有用示例。 - CSS属性的兼容性