在开发 kayura-uasp 项目时,其中的附件库管理模块,需要使用到文件上传组件。
最后通过各种比较,发现 WebUploader 这个组件很不错,还支持 md5 校验,PC端移动端,都可以支持,初步使用比较满意。
于是乎就有了下面的代码,来支持文件上传。
uploader = WebUploader.create({
swf: '${root}/res/webuploader/Uploader.swf',
server: '${root}/file/upload.json',
pick: '#upload', // 上传按钮Id
auto: true,
formData:{
folderId: selectNode.id // 上传目录Id
}
});
uploader.on('uploadFinished', function (file, response) {
_findFiles(selectNode.id); // 上传完成后刷新目录.
});
后来通过调整了 webuploader.css 样式文件,去制了添加的按钮颜色。这下就保持了绑定按钮的原汁原味了。
上面的代码功能一切正常,能正确上传文件,并且在上传完成后,刷新目录文件列表。但这并没有完,新的想法和需求随之而来,归纳为以下几个方面;
- 我没有办法看到文件的上传过程,只有静静的等待上传完成后的刷新,才知道已经上传完成。所以我需要一个显示文件上传进度条,并且只有在上传过种中才显示,上传完成后随之消失。不能破坏整体页面结构。
- 有时仅需要在一个页面中显示一个上传按钮,点击它就进行上传,完成后提供一个事件。
- 有时在表单页面中,需要多个上传按钮,并且每个按钮上传后均在按钮下面显示已上传的文件列表。显示的文件列表也可以通过参数控制来决定显示方式。
- 要是每个上传按钮,都要写 WebUploader.create 代码来处理,那么显示使用非常不友好,冗余代码会很多,封装成 jQuery 组件应该是个不错的方式。
根据上面的需求,创建了一个 juasp-uploader.js 文件,并且封装成了一个 jQuery 上传组件。那么现在要使用它来绑定上传按钮时,代码就变成了下面的样式:
// 上传按钮Id
$("#upload").uploader({
formData : {
folderId: selectNode.id // 上传目录Id
},
onFinished : function (){
_findFiles(selectNode.id); // 上传完成后刷新目录.
}
});
现在是不是简洁了很多。
如果一个表单需要绑定多种类型的附件时,也可以使用以下代码来完成。
<script type="text/javascript">
$(function(){
$("#fileUpload1").uploader({
showlist : true, // 显示列表
showinfos : false, // 不显示文件信息(默认显示)
actions : {
remove : true // 提供移除功能(仅能移除自己上传的)
},
formData : {
category: '合同', // 表单附件分类
bizId: $("#bizId").val() // 表单主键Id
}
});
$("#fileUpload2").uploader({
showlist : true, // 显示列表
formData : {
category: '归档',
bizId: $("#bizId").val()
}
});
});
</script>
<!-- 业务表单主键 Id -->
<input type="hidden" id="bizId" name="bizId" value="8F6528BEEAB411E5AD4E10BF48BBBEC9" />
<k:panel id="p1" title="列表上传(合同)" style="width: 600px; padding: 10px">
<k:linkbutton id="fileUpload1" iconCls="icon-upload" text="上传文件" />
</k:panel>
<div style="margin: 5px;"></div>
<k:panel id="p2" title="列表上传(归档)" style="width: 600px; padding: 10px;">
<k:linkbutton id="fileUpload2" iconCls="icon-upload" text="上传文件" />
</k:panel>
现在来看看最终附件上传及显示效果。

现在基本满足了开发与使用的需求。当然在这并不是最终,现在又有了新的想法与需求要去解决了。
- md5校验(秒传)还需要实现;
- 还有需要针对图片上传的预览以及浏览功能实现;
最后再看看目录已经完成的 juasp-uploader.js 文件里有些什么内容吧,它可是一个标准的 jQuery 组件。
/**
* 使用 WebUploader 的封装文件/图片上传组件.
*
* Copyright 2015-2016 the original author or authors.
* HomePage: https://kayura.net
*/
// 文件上传组件.
(function($, win) {
var $queue = null;
var MB = 1024 * 1024, GB = MB * 1024;
var icons = ['avi','bmp','bz2','db','doc','docx','exe','gif','jpg','jpge','mov',
'mp3','mp4','mpg','mpp','msi','pdf','png','ppt','pptx','rar','rdp',
'sql','tar','txt','vsd','wps','xls','xlsx','zip','htm','html','mht',
'mhtml','tif','tiff','iso','jar','rp','oom','pdm','psd','xml','xsm',
'wma','wmv','vtx','udl','reg','chm','ini'];
// 计算 Web 项目路径.
var hostPath = win.location.href.substring(0, win.location.href.indexOf(win.location.pathname));
var projectName = win.location.pathname.substring(0, win.location.pathname.substr(1).indexOf('/') + 1);
var appPath = hostPath + projectName;
function newid() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
juasp.getIconName = function(postfix){
if(!juasp.isEmpty(postfix)){
var i = icons.indexOf(postfix.toLowerCase());
if(i>=0){
return icons[i];
} else {
return "unknown";
}
} else {
return "unknown";
}
}
juasp.removefile = function (frid, fileName){
juasp.post(appPath + '/file/remove.json',
{ id : frid, isBiz : 1 },
{ success: function(r) {
var t = $("#fc_" + frid);
t.fadeOut('fast', function(){ t.remove(); });
juasp.info("文件 [" + fileName + "] 已经被移除。");
}
});
}
// ...
function init(target){
if (!WebUploader.Uploader.support()) {
var error = "上传控件不支持您的浏览器!请尝试升级flash版本或者使用Chrome引擎的浏览器。";
$(target).text(error);
return;
}
var opts = $.data(target, 'uploader').options;
var targetId = target.id;
var $target = $(target);
if ($queue == null) {
$queue = $('<div class="webuploader-filequeues-list"></div>');
$queue.appendTo("body");
}
if(opts.showlist) {
var $list = $("<div style='list-style: none;padding: 5px;'></div>");
$target.after($list);
}
if(!opts.actions.upload){
$target.remove();
}
// 下载文件列表.
if(opts.showlist) {
juasp.post(appPath + '/file/find.json',
{
bizId : opts.formData.bizId,
category : opts.formData.category,
tags : opts.formData.tags
},
{ success: function(r) {
for(var i in r.data){
appendFileList(r.data[i]);
}
}
});
}
var uploader = WebUploader.create($.extend({
pick : '#' + targetId,
formData : opts.formData
}, opts.innerOptions));
$.data(target, 'uploader', { options: opts, uploader : uploader });
uploader.on('uploadFinished', function () {
if(opts.onFinished()) {
opts.onFinished();
}
});
uploader.on('filesQueued', function (files) {
for(var i in files){
$queue.append("<li id='li_" + files[i].id + "' class='webuploader-filequeues-item'>" + files[i].name + "</li>");
}
});
uploader.on('uploadSuccess', function (file, response) {
var t = $queue.find("#li_" + file.id);
t.html("<span style='color:#A9A9A9'>" + file.name + " 上传完成.</span>");
setTimeout(function(){
t.fadeOut('slow', function(){ t.remove(); });
},3000);
});
uploader.on('uploadProgress', function (file, percentage) {
var t = $queue.find("#li_" + file.id);
if(percentage == 1){
t.html("<span style='color:#A9A9A9'>" + file.name + " 上传完成.</span>");
} else {
t.html("<span style='color:#0000FF'>" + file.name + " 上传中 " + Math.round(percentage * 100) + " %</span>");
}
});
uploader.on('error', function(type){
/**
* type {String} 错误类型。
* Q_EXCEED_NUM_LIMIT 在设置了fileNumLimit且尝试给uploader添加的文件数量超出这个值时派送。
* Q_EXCEED_SIZE_LIMIT 在设置了Q_EXCEED_SIZE_LIMIT且尝试给uploader添加的文件总大小超出这个值时派送。
* Q_TYPE_DENIED 当文件类型不满足时触发。
*/
if(type == "Q_EXCEED_NUM_LIMIT"){
juasp.errortips("添加文件时超出了本次限制的最大文件数。");
}
});
uploader.on('uploadAccept', function(o, r){
if(opts.showlist) {
if(r.type == juasp.SUCCESS) {
r.data.isUploader = true;
appendFileList(r.data);
} else {
juasp.errortips(r.message);
}
}
});
function appendFileList(data){
var html = '<li id="fc_' + data.frId + '" class="webuploader-fileitems">' ;
if(opts.showicon){
html += '<img class="webuploader-fileitem-icon" src="'+ appPath + '/res/images/types/' + juasp.getIconName(data.postfix)+ '.png">';
}
html += '<a href="' + appPath + '/file/get?id=' + data.frId + '" '
html += 'title="文件大小: ' + data.fileSize + '; 上传者: ' + data.uploaderName + ';">' + data.fileName + '</a>';
if(opts.showinfos){
html += "<span class='webuploader-fileitem-info'>(大小: " + data.fileSize + ", " + data.uploadTime + ")</span>";
}
if(opts.actions.remove && data.isUploader) {
html += '<a href="javascript:void(0)" onclick="juasp.removefile(\'' + data.frId + '\',\'' + data.fileName + '\')">';
html += '<img class="webuploader-fileitem-action-icon" src="'+ appPath + '/res/images/icons/clear.png"></a>';
}
html += '</li>';
$list.append(html);
}
}
var webuploader = function(options, param){
if (typeof options == 'string'){
return webuploader.methods[options](this, param);
}
options = options || {};
return this.each(function(){
var state = $.data(this, 'uploader');
if (state){
$.extend(true, state.options, options);
} else {
var opts = $.extend(true, {}, webuploader.defaults, options);
state = $.data(this, 'uploader', {
options: opts
});
init(this);
}
});
};
// 上传组件支持方法.
webuploader.methods = {
setFormData: function(jq, param){
var state = $.data(jq[0], 'uploader');
if (state) {
var opts = state.options;
opts.formData = $.extend(true, {}, opts.formData, param);
state.uploader.options.formData = opts.formData;
}
}
};
// 上传组件默认属性.
webuploader.defaults = {
onFinished : function () { },
onSuccess : function (file, res) { } ,
showlist : false,
showicon : true,
showinfos : true,
actions : {
upload : true,
remove : true
},
formData: { // 附件上传可接收的参数范本示例.
bizId : '', // ● 绑定业务表单Id.
category : '', // ● 该附件在表单中的分类.
folderId : '', // ● 可指定添加到的文件夹.在指定了bizId时,该值将被忽略.
serial : 0, // ● 排序码.
allowChange : 0, // ● 是否允许编辑.
isEncrypt : 0, // ● 是否加密文件.
tags : '' // ● 自定义标签,使用逗号间隔.
},
innerOptions : {
swf: appPath + '/res/webuploader/Uploader.swf',
server: appPath + '/file/upload.json',
auto: true, // 设置为 true 后,不需要手动调用上传,有文件选择即开始上传。
fileNumLimit: 999, // 验证文件总数量, 超出则不允许加入队列。
fileSizeLimit: 100 * MB, // 验证文件总大小是否超出限制, 超出则不允许加入队列。
fileSingleSizeLimit: 20 * MB, // 验证单个文件大小是否超出限制, 超出则不允许加入队列。
fileVal : "file", // 设置文件上传域的name。
method: "POST", // 文件上传方式,POST或者GET。
duplicate: true
}
};
$.fn.uploader = webuploader;
}(jQuery, window));
更多详细的代码可以下载 kayura-uasp 项目来查看,比较上传服务端是怎么开发的。
kayura-uasp 项目 Git 地址:https://github.com/KayuraTeam/kayura-uasp