一、概述
在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实现文件批量上传组件/