首页 > 代码库 > Facade模式实现文件上传(Flash+HTML5)

Facade模式实现文件上传(Flash+HTML5)

一、前言

确定了渐进式增强的上传方式,接下来我们需要将上传功能从具体的业务逻辑中剥离出来,作为公共组件供业务层调用。这就要求我们必须对业务层隐藏上传细节,只暴露统一的上传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;        },        onl oad: 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);        },        one rror: function (taskId, errorCode, errorMsg) {            alert("文件上传失败:" + errorMsg);            this.agent.onerror && this.agent.onerror(errorMsg);        },        onm ouseover: function () {        },        onm ouseout: 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 = http://www.mamicode.com/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);        },        onl oad: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="http://www.mamicode.com/jquery-1.8.3.js"></script><script src="http://www.mamicode.com/swfobject.js"></script><script src="http://www.mamicode.com/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邮箱前端团队内部分享。希望对大家有所帮助。

五、参考资料
百度前端上传组件:http://fex.baidu.com/blog/2014/04/html5-uploader/?qq-pf-to=pcqq.group

Facade模式实现文件上传(Flash+HTML5)