一、前言

确定了渐进式增强的上传方式,接下来我们需要将上传功能从具体的业务逻辑中剥离出来,作为公共组件供业务层调用。这就要求我们必须对业务层隐藏上传细节,只暴露统一的上传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. Nginx 进行性能配置

    总所周知,网络上我们购买的服务器的性能各不相同,如果采用 Nginx 的默认配置的话,无法将服务器的全部性能优势发挥出来,我们应该选择适合自己需求的配置. 当我们默认安装后 Nginx 后,我们便得到 ...

  2. Linux--CentOS7使用firewalld打开关闭防火墙与端口

    1.firewalld的基本使用 启动: systemctl start firewalld 关闭: systemctl stop firewalld 查看状态: systemctl status f ...

  3. ant实例

    <?xml version="1.0" encoding="UTF-8" ?> <project name="javaTest&qu ...

  4. Qt 学习之路 2(59):使用流处理 XML

    Qt 学习之路 2(59):使用流处理 XML 豆子 2013年7月25日 Qt 学习之路 2 18条评论 本章开始我们将了解到如何使用 Qt 处理 XML 格式的文档. XML(eXtensible ...

  5. php:// 访问各个输入/输出流

    相关 php.ini 配置 allow_url_fopen :on 默认开启 该选项为on便是激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象文件等. allow_url_inc ...

  6. 网站安装 https 证书

    1.点击进入官网  数安时代 2.点击选择你需要的ssl 证书 3.点击进入选择 免费申请 ,填写你需要 安装 ssl 证书 的域名 4.根据你的需求选择,我这里选择的是 数安时代 ,接下来按照步骤操 ...

  7. C++_类继承6-继承和动态内存分配

    如果基类使用动态内存分配,并重新定义赋值和复制构造函数,这将怎样影响派生类的实现?这个问题的答案取决于派生类的属性.如果派生类也使用动态内存分配,那就需要注意学习新的小技巧. 派生类不适用new // ...

  8. 115th LeetCode Weekly Contest Prison Cells After N Days

    There are 8 prison cells in a row, and each cell is either occupied or vacant. Each day, whether the ...

  9. C# B站的弹幕提取

    要知道B站的弹幕位置 如果只考虑视频,B站的链接格式为:https://www.bilibili.com/video/av34042815.把av后面的数字看做是唯一标记即可. 既然能够把弹幕加载出来 ...

  10. UVALive - 6952 DP 分段/隔板

    题意:商品总价按四舍五入计算,n个物品最多可分\(d+1\)段,求最小代价 \(dp[i][j]\):\(j\)个物品分\(i\)段 注意一个技巧是只在需要分出新的段时才四舍五入(旧段结算),这样就避 ...