基于第一篇的实现大文件分片上传与合并的实现,想进一步结合一下模块化,因此就有了本篇内容:大文件分片上传于合并的组件化实现
目前浏览器端比较流行的就是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方法也可以不传。