html5实现文件批量上传组件
一、概述
在html5中,相对于之前添加了不少新的元素和属性,在javascript中也添加了一些新的API,这些给我们的开发带来了很多便利。但由于各浏览器的发展步骤不一致,也导致了不同浏览器对html5支持的差异性。
二、实现原理
1.在该html5实现的文件批量上传组件中,我们主要是利用html5中的一些新属性和新API来达到我们的目的。具体如下:
(1)file input控件中的multiple属性。添加了该属性后,我们在选择文件时就可以进行多选。
(2)javascript File API和相应一些属性。利用这些新功能,我们可以获取到选择的文件集合以及文件的一些属性,如文件名,文件大小,文件类型等等。
(3)FormData。使用它可以向服务器提交key/value的键值对数据。
(4)通过XMLHttpRequest的onprogress来获取和更新文件上传进度。
上面就是用到的主要技术点,更多详细的部分可以参考源代码。
2.具体的流程。
(1)首先使用时需要提交选择按钮,上传按钮和列表显示的元素id,组件通过为选择按钮添加onmouseover事件来动态生成(如果尚未生成过)一个file input控件,并设置为透明,然后定位到鼠标可以触发的位置。所以当用户点击选择按钮时,实际上点击的是file input控件的浏览来浏览并选择文件。
(2)组件调用file控件的files来获取文件的集合,然后循环将文件显示成列表,文件名、文件类型、文件大小、文件上传状态等可以通过占位符来确定其显示与否以及显示的位置。
(3)通过为上传按钮添加click上传事件来实现上传,上传时通过FormData和Ajax逐个上传(服务端按照常规获取文件的方法来获取相应的文件并实现上传),并显示上传进度。
三、浏览器兼容性
适用浏览器:Chrome,Firefox,Safari,Opera,IE10
除了IE之外的其他浏览器开始支持的版本没有详细了解和测试,电脑上都是它们目前的新版本,可以很好的支持。
四、使用方法
具体调用方法如下(说明看注释):
window.onload = function () {
$ling.html5Upload.config = {
selectButtonId: "selectButton",
uploadButtonId: "uploadButton",
fileProgressContainerId: "processContainer",
url: "upload.ashx",
fileTypes: "|.jpg|.jpeg|.txt|",
fileSize: 300 * 1024 * 1024, //这里的限制 要跟服务端对文件大小的限制一致 或者 小于服务器文件大小的限制
//回调可以调用三个参数 总数,成功数和失败数,如果不需要可以不传
callback: function (allCount, successCount, failCount) { alert(allCount + "/" + successCount + "/" + failCount); }
};
$ling.html5Upload.init();
};
上面的配置config中还有下面列出来的5个属性,下面的值均为默认值,不配置时将显示默认值
failWord: "失败",//该文件上传失败时显示在百分比进度地方的提示显示
waitingWord: "等待", //该文件等待上传时显示在百分比进度地方的提示显示
fileTypeInvalidatedWord: "格式不支持", //格式不支持时显示在百分比进度地方的提示显示
fileSizeInvalidatedWord: "文件超出限制", //文件超出限制时时显示在百分比进度地方的提示显示
repeatSubmitWord: "请不要重复提交",//重复提交时的提示
html代码如下:
<div> <input id="selectButton" type="button" value="Select" /> <input id="uploadButton" type="button" value="Upload" /> <div id="processContainer" style="display:none;"> <div>{FileName} | {FileSize} | {FileType} | {UploadPercentage}</div> </div> </div>
具体也可以看下载里面的例子。
五、源代码
具体代码:
(function () { //公开方法 var html5Upload = { init: null }; //默认配置 html5Upload.configDefault = { selectButtonId: null, uploadButtonId: null, fileProgressContainerId: null, failWord: "失败",//该文件上传失败时显示在百分比进度地方的提示显示 waitingWord: "等待", //该文件等待上传时显示在百分比进度地方的提示显示 fileTypeInvalidatedWord: "格式不支持", //格式不支持时显示在百分比进度地方的提示显示 fileSizeInvalidatedWord: "文件超出限制", //文件超出限制时时显示在百分比进度地方的提示显示 repeatSubmitWord: "请不要重复提交", //重复提交时的提示 fileTypes: null, fileSize: null, callback: null, url:null }; //初始化事件 html5Upload.init = function () { new Upload(); }; //上传事件构造函数 function Upload() { this.config = html5Upload.config; this.config.failWord = !this.config.failWord ? html5Upload.configDefault.failWord : this.config.failWord; this.config.waitingWord = !this.config.waitingWord ? html5Upload.configDefault.waitingWord : this.config.waitingWord; this.config.repeatSubmitWord = !this.config.repeatSubmitWord ? html5Upload.configDefault.repeatSubmitWord : this.config.repeatSubmitWord; this.config.fileTypeInvalidatedWord = !this.config.fileTypeInvalidatedWord ? html5Upload.configDefault.fileTypeInvalidatedWord : this.config.fileTypeInvalidatedWord; this.config.fileSizeInvalidatedWord = !this.config.fileSizeInvalidatedWord ? html5Upload.configDefault.fileSizeInvalidatedWord : this.config.fileSizeInvalidatedWord; this.successCount = 0; this.failCount = 0; this.createFileInput(); //绑定选择按钮事件 addEvent("mousemove", document.getElementById(this.config.selectButtonId), function (obj, event) { return function (event) { obj.selectButtonMoveEvent(event); }; } (this)); //绑定上传按钮事件 addEvent("click", document.getElementById(this.config.uploadButtonId), function (obj) { return function () { if (obj.isUploaded) { alert(obj.config.repeatSubmitWord); return; } obj.isUploaded = true; obj.uploadFile(); }; } (this)); } //创建文件输入框 Upload.prototype.createFileInput = function () { var id = Math.random(); var fileInputId = ("html5UploadBox" + id).replace("0.", ""); var fileInput = document.createElement("input"); fileInput.id = this.fileInputId; fileInput.type = "file"; fileInput.multiple = true; fileInput.style.position = "absolute"; if (fileInput.filters) { fileInput.filters.alpha.opactity = 0; } else { fileInput.style.opacity = 0; } document.body.appendChild(fileInput); this.fileInput = fileInput; addEvent("change", this.fileInput, function (obj) { return function () { //重置属性值 obj.progressPercentage = null; obj.isUploaded = false; obj.successCount = 0; obj.failCount = 0; //执行列表显示 if (!obj.progressTimer) { obj.displayProgress(); } }; } (this)); }; //文件选择按钮事件 将透明的文件输入框至于鼠标点击范围 Upload.prototype.selectButtonMoveEvent = function (event) { var x = event.clientX + document.body.scrollLeft;; var y = event.clientY+ document.body.scrollTop; this.fileInput.style.top = y - 5 + "px"; this.fileInput.style.left = x + 5 - this.fileInput.offsetWidth + "px"; }; //显示文件列表 Upload.prototype.displayProgress = function (isNotRepeat) { var files = this.fileInput.files; var progressContainer = document.getElementById(this.config.fileProgressContainerId); var html; if (!this.listTemplate) { this.listTemplate = progressContainer.innerHTML; } if (!this.progressPercentage) { this.progressPercentage = new Array(); } html = this.listTemplate; var dealHTML = ""; for (var i = 0, len = files.length; i < len; i++) { if (!this.progressPercentage[i]) { this.progressPercentage[i] = this.config.waitingWord; } if (!checkFileType(this.config.fileTypes, files[i].name)) { this.progressPercentage[i] = this.config.fileTypeInvalidatedWord; } else if (!checkFileSize(this.config.fileSize, files[i].size)) { this.progressPercentage[i] = this.config.fileSizeInvalidatedWord; } var tempHTML = html.replace(/\{FileName\}/g, files[i].name); tempHTML = tempHTML.replace(/\{FileType\}/g, files[i].type); tempHTML = tempHTML.replace(/\{FileSize\}/g, getFileSizeString(files[i].size)); tempHTML = tempHTML.replace(/\{UploadPercentage\}/g, this.progressPercentage[i]); dealHTML += tempHTML; } progressContainer.innerHTML = dealHTML; progressContainer.style.display = ""; if (isNotRepeat) { return; } this.progressTimer = setTimeout(function (obj) { return function () { obj.displayProgress(); }; } (this) , 200); }; //上传文件到服务器 Upload.prototype.uploadFile = function (index) { var files = this.fileInput.files; var len = files.length; var fd = new FormData(); var request = httpRequest(); if (!index && index != 0) { index = 0; } //如果不是合法文件则跳过不上传 if (this.progressPercentage[index] != this.config.waitingWord) { index++; this.failCount++; this.uploadFile(index); } else { fd.append("ling_file_name", files[index]); request.open("POST", this.config.url, true); request.onprogress = uploadProgressEvent(this, index); request.onreadystatechange = uploadSuccessEvent(this, index, request, len); request.onerror = uploadFailEvent(this, index); request.send(fd); } }; //上传中的函数 function uploadProgressEvent(obj, index) { return function (event) { obj.progressPercentage[index] = parseInt(event.loaded * 100 / event.total) + "%"; }; } //上成功函数 function uploadSuccessEvent(obj, index, request, len) { return function () { if (request.readyState === 4 && request.status === 200) { if (request.responseText === "1") { obj.progressPercentage[index] = "100%"; obj.successCount++; } else { obj.progressPercentage[index] = obj.config.failWord; obj.failCount++; } index++; if (index > len - 1) { //结束时清除列表更新定时执行器 然后执行最后一遍 clearTimeout(obj.progressTimer); obj.progressTimer = null; obj.displayProgress(true); if (obj.config.callback) { obj.config.callback(len, obj.successCount, obj.failCount); } return; } obj.uploadFile(index); }; } } //上传失败函数 function uploadFailEvent(obj, index) { return function () { obj.progressPercentage[index] = obj.config.failWord; index++; obj.uploadFile(index); }; } //动态绑定事件 function addEvent(eventType, element, fn) { if (element && eventType && fn) { if (window.addEventListener) { element.addEventListener(eventType, fn); } else { element.attachEvent("on" + eventType, fn); } } }; //获取XMLHttpRequest function httpRequest() { try { return new XMLHttpRequest(); } catch (e) { return new ActiveXObject("Microsoft.XMLHTTP"); } } //转换文件大小为k或者M表示 function getFileSizeString(size) { if (!size && typeof (size) !== "number") { return "0k"; } if (size > 1024 * 1024) { return (size / (1024 * 1024)).toFixed(2) + "M" } else { return (size / 1024).toFixed(2) + "k"; } } //检查文件类型是否合法 function checkFileType(allowFileTypes, fileName) { if (!allowFileTypes) { return true; } if (!fileName || fileName.indexOf('.') < 1) { return false; } var extension = fileName.substring(fileName.lastIndexOf('.'), fileName.length).toLowerCase(); allowFileTypes = allowFileTypes.toLowerCase(); if (allowFileTypes.indexOf("|" + extension + "|") > -1) { return true; } return false; } //检查文件大小是否合法 function checkFileSize(allowSize, fileSize) { if (!allowSize || typeof (allowSize) != "number") { return true; } if (fileSize <= allowSize) { return true; } return false; } window.$ling = window.$ling || {}; window.$ling.html5Upload = html5Upload; })();
参考文章:http://blog.ncmem.com/wordpress/2023/12/25/html5%e5%ae%9e%e7%8e%b0%e6%96%87%e4%bb%b6%e6%89%b9%e9%87%8f%e4%b8%8a%e4%bc%a0%e7%bb%84%e4%bb%b6/
欢迎入群一起讨论