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;
        },
        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邮箱前端团队内部分享。希望对大家有所帮助。

五、参考资料

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

时间: 2024-10-22 08:42:16

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

cjhupload一个简单异步文件上传插件(html5+js)

来源:http://www.myopenresources.com/page/resource_detail_0.html?rid=370 更多文章请查看本人博客网站:http://www.myopenresources.com cjhupload是一个本人基于原生JS编写的一个文件上传插件,支持手机.电脑端,你可查看例子,或下载详细例子(请到下面的"下载地址"下载). 例子: <!DOCTYPE HTML> <html lang='zh'> <head&

求超大文件上传方案( HTML5 )

1,项目调研 因为需要研究下断点上传的问题.找了很久终于找到一个比较好的项目. 在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面. https://github.com/freewebsys/java-large-file-uploader-demo 效果: 上传中,显示进度,时间,百分比. 点击[Pause]暂停,点击[Resume]继续. 2,代码分析 原始项目: https://code.google.com/p/java-lar

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

最近在项目中中上传图片时,大约有300多K,结果报了个服务器错误,以前从未遇到过,错误的内容如下: mod_fcgid: www.111cn.net HTTP request length 132296 (so far) exceeds MaxRequestLen (131072) 查了下资料,发现fastcgi默认的请求大小为131072,于是在apache配置中添加了MaxRequestLen 配置就好了.如果你只需要修改单个虚拟主机的from:http://www.111cn.net/ph

支持多文件上传的jQuery文件上传插件Uploadify

支持多文件上传的jQuery文件上传插件Uploadify,目前此插件有两种版本即Flash版本和HTML5版本,对于HTML5版本会比较好的支持手机浏览器,避免苹果手机Safari浏览器不支持Flash,主要特性:支持多文件上传.HTML5版本可拖拽上传.实时上传进度条显示.强大的参数定制功能,如文件大小.文件类型.按钮图片定义.上传文件脚本等. Flash版本使用方法: 1.加载JS和CSS ? 1 2 3 <script src="jquery/1.7.1/jquery.min.js

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

支持多文件上传的jQuery文件上传插件Uploadify,目前此插件有两种版本即Flash版本和HTML5版本,对于HTML5版本会比较好的支持手机浏览器,避免苹果手机Safari浏览器不支持Flash,主要特性:支持多文件上传.HTML5版本可拖拽上传.实时上传进度条显示.强大的参数定制功能,如文件大小.文件类型.按钮图片定义.上传文件脚本等. Flash版本使用方法: 1.加载JS和CSS ? 1 2 3 <script src="jquery/1.7.1/jquery.min.js

Spring3文件上传,提速你的Web开发

Spring1 推出的时候可以说是不小的颠覆了J2EE 的开发,彻底把EJB打败,将J2EE开发进行简化,Spring2 推出以后完美的与多种开源框架与服务器的结合,让你对其拥抱的更紧,Spring变成了一个工具箱,一应俱全,Spring3 推出以后让开发真正的得到简单化,并且Spring3支持REST风格,采用Spring3进行开发,简单的有点过分了. 如果说我在吹嘘,那么1行有效的代码就可以完成B/S模式的文件上传,呵呵.废话少说,先看代码:清单1:springmvc-servlet.xml

多文件上传插件Stream,是Uploadify的Flash版和Html5版的结合,带进度条,并支持html5断点续传(附件上传),拖拽等功能

是根据某网的文件上传插件加工而来,支持不同平台(Windows, Linux, Mac, Android, iOS)下,主流浏览器(IE7+, Chrome, Firefox, Safari, 其他)的上传工作,当然在Html5标准下,还支持文件的断点续传功能,有效解决大文件的Web上传问题! 主要特征 1. 支持HTML5.Flash两种方式(跨域)上传 2. 多文件一起上传 3. HTML5支持断点续传,拖拽等新特性 4. 兼容性好IE7+, FF3.6+, Chrome*,Safari4+

轻量级 web 文件上传组件,支持html5,支持上传进度显示(IE10+、标准浏览器),文件拖拽,降级支持IE6+

老早就注册了博客园帐号,昨天才发现,连博客都没开,Github也是一样,深觉惭愧,赶紧潜个水压压惊`(*∩_∩*)′ 言归正传.大概许多人都会用到文件上传的功能,上传的库貌似也不少,比如(jQuery File Uploader.FineUploader.Uploadify.Baidu Web Uploader 等等),功能都很强大,代码量一般也较大.当时心想,就这么个小功能,杀鸡焉用牛刀,用库的话还得熟悉它的用法,有的需要引入额外的库,纯Flash的不考虑,还是动手造个轮子得了,至少造过之后能

轻量级 web 文件上传组件,纯js,html5、html4智能适配,支持上传进度显示(IE10+、标准浏览器)

老早就注册了博客园帐号,昨天才发现,连博客都没开,Github也是一样,深觉惭愧,赶紧潜个水压压惊`(*∩_∩*)′ 言归正传.大概许多人都会用到文件上传的功能,上传的库貌似也不少,比如(jQuery File Uploader.FineUploader.Uploadify.Baidu Web Uploader 等等),功能都很强大,代码量一般也较大.当时心想,就这么个小功能,杀鸡焉用牛刀,用库的话还得熟悉它的用法,有的需要引入额外的库,纯Flash的不考虑,还是动手造个轮子得了,至少造过之后能