基于NodeJs的Express及Webuploader实现大文件分片上传与合并(二)——组件化实现

基于第一篇的实现大文件分片上传与合并的实现,想进一步结合一下模块化,因此就有了本篇内容:大文件分片上传于合并的组件化实现

目前浏览器端比较流行的就是Requirejs,因此本篇结合Requirejs,进一步将功能组件化。

其实利用Requirejs改造之前的功能是比较容易的,按照Requirejs官方的文档有以下几步:

1)下载Requirejs。

2)在view模板文件中改造之前的js引用方式。

3)将原先的逻辑功能抽象成一个组件,并在页面调用的Requirejs统一入口文件中调用即可。

下面就详细介绍一下2、3步,所有的代码见我的GitHub开发分支。

第2步很简单,贴一下代码之前upload.jade改造后的代码,其中html dom部分不变,只是把多个script改成requirejs引入的方式:

extends ../layout/front

block content
    link(rel="stylesheet" type="text/css" href="plugins/webuploader/webuploader.css")
    link(rel="stylesheet" type="text/css" href="css/front/upload.css")

    #uploader.wu-example
        .dndArea
            .file-select
                #picker 选择文件
                p 或将文件拖到这里
        // 用来存放文件信息
        #thelist.uploader-list
        button#ctlBtn.btn.btn-default 开始上传

    script(src="js/require-2.1.11.min.js" defer async="true" data-main="js/front/upload.js")
第3步比较重要,也是关键。首先抽象逻辑功能为一个组件,这势必要预先定义一下requirejs的config,我写到了js/front/upload.js里面,同时调整了组件的目录结构,贴一下配置:

requirejs.config({
    baseUrl: 'js',
    paths: {
        jquery: 'jquery-1.8.3.min',
        webuploader: '../plugins/webuploader/webuploader',
        md5: 'md5.min',
    }
});
其次就是要抽象改造功能为Requirejs的组件,改造的思路我借鉴了js的prototype原型模式,具体的原型模式可以参见我的另一篇文章:JavaScript面向对象编程之封装(一)。

采用原型模式的基本框架(套路)如下:

function ClassName(a, b)
{
        this.xxx = a;
        this.yyy = b;
}
ClassName.prototype.zzz = function() {
        console.log('zzz');
};

var obj = new ClassName('x', 'y');
根据原型模式封装上传组件:
define(['jquery', 'md5', 'webuploader'], function ($, md5, WebUploader) {

    /**
     * 类
     * @constructor
     */
    function Uploader() {
        //-------------公有属性-------------
        this.config = {};
        this.uploader = {};
    }

    // -------------公共方法--------------

    // 组件初始化(公有方法)
    Uploader.prototype.init = function (cfg) {
        if (!this.support()) {
            return false;
        }

        //-------------私有属性-------------
        var _config = {
            // 选完文件后,是否自动上传。
            auto: false,

            // swf文件路径
            swf: '../../webuploader/Uploader.swf',

            // 文件接收服务端。
            server: '/upload_chunks',

            // 选择文件的按钮。可选。
            // 内部根据当前运行是创建,可能是input元素,也可能是flash.
            pick: {
                id: '#picker',
                label: '点击选择文件'
            },

            // 配置压缩的图片的选项
            compress: false,

            // 拖拽配置
            dnd: '.dndArea',
            disableGlobalDnd: false,

            // 此功能为通过粘贴来添加截屏的图片。建议设置为document.body
            paste: document.body,

            // 分片上传配置
            chunked: true,// 开起分片上传。
            chunkSize: Math.pow(1024, 2),// 单位字节(Byte)
            threads: 1,//上传并发数

            // 不压缩image, 默认如果是jpeg,文件上传前会压缩一把再上传!
            resize: false
        };

        //-----------------私有方法-----------------

        // 初始化配置和对象
        function _initConfig(cfg) {
            this.config = $.extend(_config, cfg);
            this.uploader = WebUploader.create(this.config);
        }

        // 初始化对象事件
        function _initEvent() {
            var that = this;

            // 当有文件被添加进队列的时候
            this.uploader.on('fileQueued', function (file) {
                var $list = $(".uploader-list");

                var item = '
' + '
' + '

' + file.name + '

' + '

等待上传...

' + '
' + '
'; $list.append(item); }); // 某个文件开始上传前触发,一个文件只触发一次 this.uploader.on('uploadStart', function (file) { that.uploader.options.formData.guid = md5([file.id, file.name, file.size, file.type, file['__hash']].join('')); }); // 文件上传过程中创建进度条实时显示。 this.uploader.on('uploadProgress', function (file, percentage) { var $li = $('#' + file.id), $percent = $li.find('.progress .progress-bar'); // 避免重复创建 if (!$percent.length) { var progress = '
' + '
' + '
' + '
'; $percent = $(progress).prependTo($li).find('.progress-bar'); } $li.find('p.state').text('上传中'); $percent.css('width', percentage * 100 + '%'); }); this.uploader.on('uploadSuccess', function (file) { // console.log(file); // 如果是分片上传,文件上传成功后执行分片合并并返回Get文件的url if (that.uploader.options.chunked) { $.post('/merge_chunks', { 'hash': md5([file.id, file.name, file.size, file.type, file['__hash']].join('')), 'name': file.name, 'size': file.size }, function (data) { if (data.status) { $('#' + file.id).find('p.state').text('已上传'); $('#' + file.id).find('.progress-bar').css({ 'background-image': 'url(' + data.url + ')', 'background-size': 'cover', 'background-repeat': 'no-repeat' }); } else { $('#' + file.id).find('p.state').text('上传错误!'); } }, 'json'); } }); this.uploader.on('uploadError', function (file) { $('#' + file.id).find('p.state').text('上传出错'); }); this.uploader.on('uploadComplete', function (file) { // $('#' + file.id).find('.progress').fadeOut(); }); } _initConfig.apply(this, [cfg]); _initEvent.apply(this); }; // 组件支持 Uploader.prototype.support = function () { try { if (!WebUploader.Uploader.support()) { alert('Web Uploader 不支持您的浏览器!如果你使用的是IE浏览器,请尝试升级 flash 播放器'); throw new Error('WebUploader does not support the browser you are using.'); } return true; } catch (e) { if (e instanceof Error) { console.error(e.message); } else { console.error('Error!'); } return false; } }; // 开始下载 Uploader.prototype.startUpload = function () { if (!this.support()) { return false; } this.uploader.upload(); }; return new Uploader(); });
这里,用到了私有属性和方法进行初始化,将Webuploader实例作为封装类的公共属性。

接下来让我们试着调用该组件,我写在upload.js里:

requirejs(['modules/uploader'], function (Uploader) {
    Uploader.init({
        pick: {
            id: '#picker',
            label: '选择文件'
        },
        threads: 2,
    });

    $('#ctlBtn').on('click', function (event) {
        event.stopPropagation();
        Uploader.startUpload();
    });
});
其中Uploader.init方法也可以不传。
至此,整体的功能模块化改造完毕,还是相对比较简单的,关键点在于模块化组件的实现。
另外,也许你有更好的封装模块化组件方式,我的仅供参考交流,不对之处还望大神们多多指正。

你可能感兴趣的:(Web前端,Nodejs,Requirejs)