一、前言

确定了渐进式增强的上传方式,接下来我们需要将上传功能从具体的业务逻辑中剥离出来,作为公共组件供业务层调用。这就要求我们必须对业务层隐藏上传细节,只暴露统一的上传API。这时候大家是不是跟我一样想到了Facade模式?
 
二、Facade模式实现文件上传,代码示例:

/*
上传组件,IE浏览器默认flash上传,其它浏览器html5
示例:
var fileUpload = new FileUpload({
container: document.getElementById("uploadBtn"),
onselect: function (files) {
var self = this;
$(files).each(function (i, n) {
updateUI(n);
});
setTimeout(function () { //异步,等待onselect函数return后才能调用upload
self.upload();
}, 10);
},
onprogress: function (fileInfo) {
updateUI(fileInfo);
},
oncomplete: function (fileInfo, responseText) {
updateUI(fileInfo); }
});
*/
function FileUpload(options) {
var uploader=null;
if (options) {
//为什么要多创建一级div容器?flash 的activex创建后,再改变位置会引起activex对象失效,所以要在创建前就定好位
var div = document.createElement("div");
div.id = "flashUploadDiv";
document.body.appendChild(div);
var c = $(options.container);
//绝对定位到上传按钮的坐标,flash本身为透明遮罩
$(div).css({
position: "absolute",
left: c.offset().left + "px",
opacity:0,
top: c.offset().top + "px"
}); if ($.browser.msie || options.uploadType == "flash") { //flash上传方式
var url = "Richinfo_annex_upload.swf";
var so = new SWFObject(url, "flashupload", c.width(), c.height());
so.addParam("wmode", "transparent");
so.write("flashUploadDiv"); options.activexObj = document.getElementById("flashupload"); window.JSForFlashUpload = new FlashUpload(options);
uploader = JSForFlashUpload; } else { $(div).html(['<form style="" enctype="multipart/form-data" id="fromAttach" method="post" action="" target="frmAttachTarget">',
'<input style="height: ', c.height(), 'px;width:', c.width(), 'px" type="file" name="uploadInput" id="uploadInput" multiple="true">',
'</form>',
'<iframe id="frmAttachTarget" style="display: none" name="frmAttachTarget"></iframe>'].join(""));
options.uploadInput = document.getElementById("uploadInput");
uploader = new Html5Upload(options); }
} this.upload = function () {//触发上传请求
//alert("uploader.load");
uploader.upload();
},
this.cancel = function () {//取消上传
uploader.cancel();
}
this.getUploadFiles = function () {//获取上传队列
uploader.getUploadFiles();
} $.extend(options, this);//继承FileUpload的能力
}
var FlashUpload = function(options){

    var resultObject = {
activexObj: options.activexObj,
upload:function(){
this.activexObj.uploadAll();
},
cancel: function () {
this.activexObj.cancel();
},
getUploadUrl: function () {
return this.agent.getUploadUrl();
},
getUploadFiles: function () {
return this.uploadFiles;
},
onload: function (param) {
this.agent = {};
if (options) {
this.agent = options;
}
param["filter"] = ["images图片(*.jpg;*.png;*.bmp)", "video(*.flv;*.avi;*.rmvb)"];
param["uploadFieldName"] = "filedata";
//options["filter"] = ["eml邮件(*.eml)"];
//options["filter"] = ["所有文件(*.*)"];
return param;
},
onselect: function (xmlFileList, jsonFileList) { for (var i = 0; i < jsonFileList.length; i++) {
jsonFileList[i].fileName = decodeURIComponent(jsonFileList[i].fileName);
jsonFileList[i].state = "waiting";
/*if (jsonFileList[i].fileSize > 100000) { //大于100K不上传
jsonFileList.splice(i, 1);
i--;
}*/
}
//uploadView.onselect(jsonFileList);
this.agent.onselect && this.agent.onselect(jsonFileList); this.uploadFiles = jsonFileList;
return jsonFileList;
},
onprogress: function (taskId, sendedSize, uploadSpeed, fileInfo) {
fileInfo.taskId = taskId;
fileInfo.sendedSize = sendedSize;
fileInfo.percent = Math.round((sendedSize / fileInfo.fileSize) * 100);
fileInfo.state = "uploading";
fileInfo.fileName = decodeURIComponent(fileInfo.fileName);//防止乱码,flash里面做了encode
//alert(fileInfo.percent);
this.agent.onprogress && this.agent.onprogress(fileInfo);
},
oncomplete: function (taskId, responseText, fileInfo) {
fileInfo.taskId = taskId;
fileInfo.state = "complete";
fileInfo.fileName = decodeURIComponent(fileInfo.fileName);//防止乱码,flash里面做了encode
this.agent.oncomplete && this.agent.oncomplete(fileInfo, responseText);
},
onerror: function (taskId, errorCode, errorMsg) {
alert("文件上传失败:" + errorMsg);
this.agent.onerror && this.agent.onerror(errorMsg);
},
onmouseover: function () { },
onmouseout: function () { },
onclick: function () {
return true;//返回false不会弹出文件选择框
//alert("onclick");
} }
return resultObject;
}
var Html5Upload = function (options) {
var resultObject = {
uploadInput: null,
currentFile: null,
uploadFiles:[],//待上传的文件
completeFiles:[],//已完成的文件
init: function () {
var self = this;
this.agent = options;
this.uploadInput = options.uploadInput;
this.uploadInput.onclick = this.onclick;
this.uploadInput.onchange = function () {
var files = this.files;
var result = [];
for (var i = 0; i < files.length; i++) {
console.log(files[i]);
result.push({
fileName: files[i].name,
fileSize: files[i].size,
fileData: files[i],
state : "waiting",
taskId: Math.random().toString().substr(2)
});
}
self.uploadFiles = result;
self.onselect(result);
}
},
getFileUploadXHR: function () { //单例
if (!window.fileUploadXHR) {
fileUploadXHR = new XMLHttpRequest();
}
this.xhr = window.fileUploadXHR;
return fileUploadXHR;
},
getUploadUrl: function () { //获取上传地址
return this.agent.getUploadUrl();
},
getUploadFiles:function(){ //获取上传队列
return this.uploadFiles.concat(this.completeFiles);
},
upload: function () {//开始上传请求
this.uploadNextFile();
},
cancel:function(){ //取消上传
this.xhr.abort();
},
uploadNextFile: function () { //每个上传文件会触发
var fileInfo = this.uploadFiles.shift();
this.completeFiles.push(fileInfo); //存入已完成列表
this.currentFile = fileInfo;
if (fileInfo) {
var self = this;
var xhr = this.getFileUploadXHR(); xhr.upload.onabort = function (oEvent) { };
xhr.upload.onerror = function (oEvent) { self.onerror(oEvent); };
xhr.upload.onload = function (oEvent) { self.onload(oEvent); };
xhr.upload.onloadend = function (oEvent) { };
xhr.upload.onloadstart = function (oEvent) { };
xhr.upload.onprogress = function (oEvent) {
console.log(oEvent);
fileInfo.state = "uploading";
fileInfo.sendedSize = oEvent.position;
fileInfo.percent = Math.round((oEvent.position / oEvent.total) * 100);
self.onprogress(fileInfo);
};
//xhr.ontimeout = function(oEvent){This.ontimeout(oEvent);};
xhr.onreadystatechange = function (oEvent) {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var responseText = xhr.responseText;
self.oncomplete(fileInfo);
}
}
}; var url = this.getUploadUrl();
xhr.open("POST", url, true); //xhr.timeout = this.timeout; //timeout
function getFormData(fileInfo) {
var formData = new FormData();
formData.append("filedata", fileInfo.fileData);
return formData;
}
var fd = getFormData(fileInfo);
xhr.send(fd);
}
},
onclick: function () { },
onselect:function(files){
this.agent.onselect && this.agent.onselect(files);
},
onload:function(e){
},
onprogress: function (fileInfo) {
this.agent.onprogress && this.agent.onprogress(fileInfo);
},
oncomplete: function (fileInfo) {
fileInfo.state = "complete";
this.agent.oncomplete && this.agent.oncomplete(fileInfo);
this.uploadNextFile();
}
} resultObject.init();
return resultObject;
}

三、调用示例:

<html>
<head>
<script src="jquery-1.8.3.js"></script>
<script src="swfobject.js"></script>
<script src="upload.js"></script>
</head>
<body>
<div style="position:absolute">
</div>
<ul id="uploadList"></ul>
</body>
<script>
function updateUI(fileInfo) {
var ul = $("#uploadList");
switch (fileInfo.state) {
case "waiting":
ul.append("<li taskId='" + fileInfo.taskId + "'>" + fileInfo.fileName + "(等待上传...)</li>");
break;
case "uploading":
ul.find("li[taskId=" + fileInfo.taskId + "]").html(fileInfo.fileName + "(" + fileInfo.percent + "%)");
break;
case "complete":
ul.find("li[taskId=" + fileInfo.taskId + "]").html(fileInfo.fileName + "(完成)");
break;
}
} var fileUpload = new FileUpload({
container: document.getElementById("uploadBtn"),
//uploadType:"flash",
getUploadUrl: function () {
return "upload.ashx";
},
onselect: function (files) {
var self = this;
$(files).each(function (i, n) {
updateUI(n);
});
setTimeout(function () { //异步,等待onselect函数return后才能调用upload
self.upload();
}, 10);
},
onprogress: function (fileInfo) {
updateUI(fileInfo);
},
oncomplete: function (fileInfo, responseText) {
updateUI(fileInfo);
console.log(this.getUploadFiles());
}
});
</script>
</html>

四、结束语

以上源码仅提供上传组件化思路,实际应用中要考虑更多,比如:弱网络环境下需要分块上传,断点续传,异常情况下的日志上报等等。

以上源码非本人原创,代码来源于139邮箱前端团队内部分享。希望对大家有所帮助。

五、参考资料

Facade模式实现文件上传(Flash+HTML5)的更多相关文章

  1. WCF 使用Stream模式进行文件上传 --节选自Packt.Net.Framework.4.5.Expert.Programming.Cookbook

    使用Stream上传文件 文件上传功能是web程序/服务上常用和必须的功能,WCF也不例外.在4.0版本之前,WCF仅仅提供了buffered模式上传文件.从4.0版本之后,WCF开始提供了Strea ...

  2. java实现文件上传--flash上传

    1.http请求的头信息是“application/octet-stream”,request body 是二进制的flash图片流 2.把流中的信息读入到文件中 代码如下,代码分三个部分: ---- ...

  3. PHP fastcgi模式大文件上传500错误

    最近在项目中中上传图片时,大约有300多K,结果报了个服务器错误,以前从未遇到过,错误的内容如下: mod_fcgid: www.111cn.net HTTP request length 13229 ...

  4. 文件上传之Html5 + jQuery上传、asp.net web api接收

    HTML: <div> <label for="fileUpload"> 选择文件 </label> <br/> <input ...

  5. asp.net 文件上传 Uploadify HTML5 带进度条

    参考的https://www.cnblogs.com/lvdabao/p/3452858.html这位,在此基础上略有修改: 1.根据Layer,将上传附件做成弹窗显示,引入frame弹窗,在项目当中 ...

  6. 可接受多个值的文件上传字段HTML5新特性

    <input type="file" id="input"  multiple="multiple"> 主要是多了个multip ...

  7. 强大的支持多文件上传的jQuery文件上传插件Uploadify

    支持多文件上传的jQuery文件上传插件Uploadify,目前此插件有两种版本即Flash版本和HTML5版本,对于HTML5版本会比较好的支持手机浏览器,避免苹果手机Safari浏览器不支持Fla ...

  8. jQuery文件上传插件Uploadify(转)

    一款基于flash的文件上传,有进度条和支持大文件上传,且可以多文件上传队列. 这款在flash的基础上增加了html5的支持,所以在移动端也可以使用. 由于官方提供的版本是flash免费,html5 ...

  9. TZ_06_SpringMVC_传统文件上传和SpringMVC文件上传方式

    1.传统文件上传方式 <!-- 文件上传需要的jar --> <dependency> <groupId>commons-fileupload</groupI ...

随机推荐

  1. python创建virtualenv虚拟环境

    pip install virtualenv virtualenv env_py36_crawl env_py36_crawl\Scripts\activate deactivate pip free ...

  2. IOS中NSUserDefaults的用法

    NSUserDefaults适合存储轻量级本地数据,比如要保存用户登陆的用户名.密码,使用NSUserDefaults是首选.下次再登陆的时候就可以直接从NSUserDefaults里面读取上次登陆的 ...

  3. 19.阻止事件冒泡e.stopPropagation();

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. java技术

    线程池的原理及实现:https://blog.csdn.net/hsuxu/article/details/8985931 Java高级工程师面试题总结及参考答案:https://www.cnblog ...

  5. 利用EFCore 封装Repository(可扩展不同数据的sql操作)

    本篇是对EFCore 进行下封装并实现基本的增删改查的同步异步方法及针对不同数据库的批量插入.sql语句直接操作数据库: 一. 先定义基础仓储接口IRepository public interfac ...

  6. sql的编写需要注意优化

    使用limit对查询结果的记录进行限定 避免select *,将需要查找的字段列出来 使用连接(join)来代替子查询 拆分大的delete或insert语句 可通过开启慢查询日志来找出较慢的SQL ...

  7. Win10 修改 开始 菜单样式..

    因为不是平板,所以改成了这个样子 下面说步骤... 打开 菜单栏位置... 将快捷方式 拷贝到 里面 来... 快捷方式 以 #开头.是为了 让其排列在最前面.... 快捷方式有个技巧...快捷方式  ...

  8. Linux 未安装vi如何编辑文件

    sed -i "s/搜索内容/替换内容/g" 文件名

  9. 查找表,Two Sum,15. 3Sum,18. 4Sum,16 3Sum Closest,149 Max points on line

    Two Sum: 解法一:排序后使用双索引对撞:O(nlogn)+O(n) = O(nlogn) , 但是返回的是排序前的指针. 解法二:查找表.将所有元素放入查找表, 之后对于每一个元素a,查找 t ...

  10. 114th LeetCode Weekly Contest Array of Doubled Pairs

    Given an array of integers A with even length, return true if and only if it is possible to reorder ...