在开发 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