使用 HTML5 和客户端脚本进行轻量级多文件上传,带缩略图预览






4.93/5 (16投票s)
本文介绍如何在页面中添加完全客户端的多文件上传功能。包括一次选择多个文件,从不同文件夹中选择,应用验证,以缩略图显示文件,以及从列表中移除文件。
引言
将文件作为附件上传是常见场景,也是许多 Web 应用程序的重要组成部分。
我曾遇到这样的需求:需要在 HTML 页面上实现一个多文件上传功能,并一次性提交到服务器。
此解决方案的优点在于它完全在客户端运行。您可以一次选择多个文件,从不同文件夹中选择,应用验证,以缩略图预览显示所选文件,并允许您从列表中删除文件。因此,它非常轻量级,因为所有开销都保留在客户端。
解决方案概要
- 一次选择多个文件
- 从不同文件夹中选择
- 对文件扩展名、文件大小和上传文件数量应用验证
- 在缩略图预览中列出所选文件
- 通过允许移除文件的选项过滤所选文件
- 将所选和过滤后的文件列表保存到 JavaScript 数组中,以便提交到服务器
背景
您需要对 HTML5、CSS 和客户端脚本(JavaScript 和 JQuery)有基本了解。
Using the Code
技术介绍
HTML5 通过 File API 提供了一种处理本地文件的标准方式。File API 可用于创建图片的缩略图预览,您还可以使用客户端逻辑来验证上传文件的类型、大小和数量。
选择文件
我使用了标准 HTML5 多文件上传控件。这是上传文件元素最直接的方式。
<input id="files" multiple="multiple" name="files[]" type="file" />
为了让文件上传控件具有现代感,我为文件上传父 span 添加了样式表。
<span class="btn btn-success fileinput-button">
<span>Select Attachment</span>
<input accept="image/jpeg, image/png, image/gif,"
id="files" multiple="multiple" name="files[]" type="file" />
</span>
用户选择后;JavaScript 将选定的 File 对象列表返回为 FileList
。我为文件上传控件添加了一个事件处理程序来访问 FileList
属性。
document.addEventListener("DOMContentLoaded", init, false);
function init() {
document.querySelector('#files').addEventListener('change', handleFileSelect, false);
}
然后,事件处理程序函数
function handleFileSelect(e) {
//to make sure the user select file/files
if (!e.target.files) return;
//To obtain a File reference
var files = e.target.files;
// Loop through the FileList and then to render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++)
{
//instantiate a FileReader object to read its contents into memory
var fileReader = new FileReader();
// Closure to capture the file information and apply validation.
fileReader.onload = (function (readerEvt) {
return function (e) {
//Apply the validation rules for attachments upload
ApplyFileValidationRules(readerEvt)
//Render attachments thumbnails.
RenderThumbnail(e, readerEvt);
//Fill the array of attachment
FillAttachmentArray(e, readerEvt)
};
})(f);
// Read in the image file as a data URL.
// readAsDataURL: The result property will contain the file/blob's data encoded as a data URL.
// More info about Data URI scheme https://en.wikipedia.org/wiki/Data_URI_scheme
fileReader.readAsDataURL(f);
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
}
图片的缩略图预览
用户选择后,我们对文件调用 fileReader.readAsDataURL()
,并通过将 'src
' 属性设置为数据 URL 来渲染缩略图。
//Render attachments thumbnails.
function RenderThumbnail(e, readerEvt)
{
var li = document.createElement('li');
ul.appendChild(li);
li.innerHTML = ['<div class="img-wrap"> <span class="close">×</span>
<img class="thumb" src="', e.target.result, '" title="', escape(readerEvt.name),
'" data-id="',readerEvt.name, '"/></div>'].join('');
var div = document.createElement('div');
div.className = "FileNameCaptionStyle";
li.appendChild(div);
div.innerHTML = [readerEvt.name].join('');
document.getElementById('Filelist').insertBefore(ul, null);
}
移除文件
我通过在每个图片预览中添加 <span class="close">×</span>
(&-times; 在 HTML 代码中表示 x)来添加移除文件的功能。因此,当用户点击红色的 (x) 时,它将被移除,通过调用下面的 Jquery 函数实现。
//To remove attachment once user click on x button
jQuery(function ($) {
$('div').on('click', '.img-wrap .close', function () {
var id = $(this).closest('.img-wrap').find('img').data('id');
//to remove the deleted item from array
var elementPos = AttachmentArray.map(function (x) { return x.FileName; }).indexOf(id);
if (elementPos !== -1) {
AttachmentArray.splice(elementPos, 1);
}
//to remove image tag
$(this).parent().find('img').not().remove();
//to remove div tag
$(this).parent().find('div').not().remove();
//to remove div tag
$(this).parent().parent().find('div').not().remove();
//to remove li tag
var lis = document.querySelectorAll('#imgList li');
for (var i = 0; li = lis[i]; i++) {
if (li.innerHTML == "") {
li.parentNode.removeChild(li);
}
}
});
}
)
验证
我对上传文件应用了以下三条验证规则:
1. 文件类型
允许的文件类型为:(jpg/png/gif)
//To check file type according to upload conditions
function CheckFileType(fileType) {
if (fileType == "image/jpeg") {
return true;
}
else if (fileType == "image/png") {
return true;
}
else if (fileType == "image/gif") {
return true;
}
else {
return false;
}
return true;
}
2. 文件大小
每个上传文件的大小不应超过 300 KB。
//To check file Size according to upload conditions
function CheckFileSize(fileSize) {
if (fileSize < 300000) {
return true;
}
else {
return false;
}
return true;
}
3. 文件数量
上传文件的数量不应超过 10 个。
//To check files count according to upload conditions
function CheckFilesCount(AttachmentArray) {
//Since AttachmentArray.length return the next available index in the array,
//I have used the loop to get the real length
var len = 0;
for (var i = 0; i < AttachmentArray.length; i++) {
if (AttachmentArray[i] !== undefined) {
len++;
}
}
//To check the length does not exceed 10 files maximum
if (len > 9) {
return false;
}
else
{
return true;
}
}
然后,根据验证规则显示错误消息。
//Apply the validation rules for attachments upload
function ApplyFileValidationRules(readerEvt)
{
//To check file type according to upload conditions
if (CheckFileType(readerEvt.type) == false)
{
alert("The file (" + readerEvt.name + ") does not match the upload conditions,
You can only upload jpg/png/gif files");
e.preventDefault();
return;
}
//To check file Size according to upload conditions
if (CheckFileSize(readerEvt.size) == false)
{
alert("The file (" + readerEvt.name + ") does not match the upload conditions,
The maximum file size for uploads should not exceed 300 KB");
e.preventDefault();
return;
}
//To check files count according to upload conditions
if (CheckFilesCount(AttachmentArray) == false)
{
if (!filesCounterAlertStatus)
{
filesCounterAlertStatus = true;
alert("You have added more than 10 files. According to upload conditions,
you can upload 10 files maximum");
}
e.preventDefault();
return;
}
}
关注点
客户端的最终输出将是一个 JavaScript 数组。您可以根据您的解决方案架构将其发送到服务器端。例如,您可以将其作为 JSON 对象发送,或将其保存在 HTML 隐藏字段中并从中读取。
对于上传规则(如文件数量、文件大小和文件类型),我建议将其保留在配置文件中并从中读取。
如果您处理的是大附件,则可以添加 HTML5 也支持的进度条。它可以让您监控文件读取的进度;这对于大文件、捕获错误以及确定读取何时完成都很有用。有关更多信息,请阅读此文。
浏览器兼容性
此控件已在最新版本的 Firefox、Internet Explorer、Chrome 和 Safari 上进行了测试
“对于旧版浏览器,您可以检查浏览器是否完全支持 File API,如下所示:”
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob)
{
// Great success! All the File APIs are supported.
}
else
{
alert('The File APIs are not fully supported in this browser.');
}
//source: https://www.html5rocks.com/en/tutorials/file/dndfiles/
最后
我已尽力使代码易于理解。如有进一步改进,欢迎提出任何评论、想法和建议。如果这对您有帮助,请“投票”。
参考文献
- https://www.html5rocks.com/en/tutorials/file/dndfiles/
- http://blog.teamtreehouse.com/using-inline-block-to-display-a-product-grid-view
- https://www.raymondcamden.com/2013/09/10/Adding-a-file-display-list-to-a-multifile-upload-HTML-control
- http://stackoverflow.com/questions/31065075/array-length-gives-incorrect-length
- https://www.raymondcamden.com/2014/04/14/MultiFile-Uploads-and-Multiple-Selects-Part-2
- https://davidwalsh.name/multiple-file-upload
历史
- 版本 1.0:2017 年 10 月 12 日