65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (16投票s)

2017年10月12日

CPOL

3分钟阅读

viewsIcon

66940

downloadIcon

4880

本文介绍如何在页面中添加完全客户端的多文件上传功能。包括一次选择多个文件,从不同文件夹中选择,应用验证,以缩略图显示文件,以及从列表中移除文件。

引言

将文件作为附件上传是常见场景,也是许多 Web 应用程序的重要组成部分。
我曾遇到这样的需求:需要在 HTML 页面上实现一个多文件上传功能,并一次性提交到服务器。

此解决方案的优点在于它完全在客户端运行。您可以一次选择多个文件,从不同文件夹中选择,应用验证,以缩略图预览显示所选文件,并允许您从列表中删除文件。因此,它非常轻量级,因为所有开销都保留在客户端。

解决方案概要

  1. 一次选择多个文件
  2. 从不同文件夹中选择
  3. 对文件扩展名、文件大小和上传文件数量应用验证
  4. 在缩略图预览中列出所选文件
  5. 通过允许移除文件的选项过滤所选文件
  6. 将所选和过滤后的文件列表保存到 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/

最后

我已尽力使代码易于理解。如有进一步改进,欢迎提出任何评论、想法和建议。如果这对您有帮助,请“投票”。

参考文献

历史

  • 版本 1.0:2017 年 10 月 12 日
© . All rights reserved.