# JSP附件组件

# 前言

附件上传下载组件,是在JSP开发规范基础上实现的前后端附件互通的模块组件,只要按照JSP开发规范,正确引入common_header.jsp、common_footer.jsp,则可以调用本组件。

# 应用范围

  • 协同、公文等模块附件区文件的渲染及上传操作。

  • 文档管理/知识管理应用的用户上载文件的管理

# 附件管理

附件组件的使用分为前端和后端,两部分配合使用才能实现完整的附件相关功能。在开发时需要分别编写JSP前端、Java后端代码。

  • 前端负责界面展现
  • 后端负责附件的保存、获取相关逻辑

附件上传分为下面几种方式:

  • 标准方式:即附件的显示、保存等功能不需要调用者关系,组件会完成所有功能。调用者只按要求调用几个接口即可。使用方法参考下面3.2.1

  • 扩展方式:即组件只完成部分功能,剩余功能需要调用者来处理。适用于上传、解析后不在使用该文件。即上传后对该文件直接解析处理,并不返回调用页,在调用者处理完后返回自己期望的页面。

  • 一页多个上传组件:即一个页面做多处需要上传文件,每处上传的属性和显示的位置不同。标准方式和扩展方式都支持。

  • js动态创建方式:上面三种都依赖与页面完整加载,对于页面不重新加载,而是通过js操作的需使用此方式。例如行为动态生成,每行都包含一个上传组件,需要通过js方法动态生成。

# 附件上传-标准方式

(1)编写前端代码:

1、声明一个附件上传组件;

2、在对应的菜单或者按钮上调用 insertAttachment() 方法

3、在自己的页面中submit form时必须使用分区方式提交。

<!-- 附件上传组件区域start -->
<div id="attachmentTR" style="display:none;">${ctp:i18n("common.attachment.label")}:(<span id="attachmentNumberDiv"></span>)</div>
<div class="comp" comp="type:'fileupload',applicationCategory:'1',canDeleteOriginalAtts:false,originalAttsNeedClone:false" attsdata='${attachmentsJSON}'>
<!-- 附件上传组件区域end -->

<!-- 定义触发按钮,点击触发附件上传start -->
<input type="button" onclick="insertAttachment()" value="上传附件">
<!-- 定义触发按钮,点击触发附件上传end -->
  • div id="attachmentTR"是显示附件数量区域,如果要显示附件数量,请在需要显示附件数量的位置放置。样式可以改,里面的id都不能修改。
  • comp属性值为:type:'fileupload',表示附件组件。
  • 如果一个页面只有一个上传组件,则可以使用insertAttachment()执行上传,如果一个页面有多个上传组件则需要调用insertAttachmentPoi(attachmentTrId属性的值)方法,该方法需要和attachmentTrId属性配合使用。
  • insertAttachment()是平台内置方法,执行此函数则进行上传,不用开发自己封装函数。

上传附件组件comp支持如下属性:

属性 说明 实例
type 指当前为上传文件组件,值为固定值 type:'fileupload
attsdata 指原有已上传的附件,需要把原有的附件显示出来。 这个属性对应的值来自于Controller,从后端参考标准接口获取附件对象字符串,参考示例
applicationCategory 应用分类,统一在ApplicationCategoryEnum定义 applicationCategory:'1'
canDeleteOriginalAtts 指能否删除原有的附件,如转发协同不能删除原有附件 canDeleteOriginalAtts:false
originalAttsNeedClone 是否需要复制原有附件,常用在模板调用 originalAttsNeedClone:false
extensions 允许上传的文件类型,用文件会后缀表示,多个类型用逗号分隔(,) extensions:'txt,jpg'
maxSize 上传文件的大小限制,单位为字节 maxSize:10240000
isEncrypt 对上传的文件是否加密,true为加密;false为不加密 isEncrypt:false
quantity 最多上传文件个数,不使用该参数事默认是5 quantity:1
attachmentTrId 一页支持多个附件上传组件时使用,和insertAttachmentPoi('poi77')方法配合使用 在方法中传入该属性的值。 attachmentTrId:poi77
callMethod 在不改变使用过程的情况下,在上传附件后回调该方法。 此属性依赖takeOver属性,takeOver值为false 注意!方法名必须用引号括起来作为字符串。具体返回值请参考扩展方式中的说明。 callMethod:'testCallBack' takeOver:false
takeOver 是否接管附件上传(即弹出窗口关闭)之后的逻辑。 该属性和callMethod配合使用,如果不使用回调方法,可以不使用该属性; 否则属性值应为false takeOver:false
showReplaceOrAppend 是否显示“追加”、“覆盖”radio。即,在上传页面中会提供如下元素, 业务模块根据下面元素确定相关逻辑。 showReplaceOrAppend:true
firstSave 是否先保存附件表信息,ture为先保存,false或者无该属性为后保存。 并且为ture时,回调方法得到的参数值为包含Attachment 的js 数组,否则为逗号(,)分隔的文件ID字符串。 这种情况下后端应该调用附件更新接口,目的是关联业务数据域附件数据。 firstSave:true

(2)后端上传附件代码逻辑:

后端负责对附件信息的存储,其实如果前端JSP编写的是标准的附件组件,则后端附件存储是不需要业务组自己写代码

在触发前端insertAttachment()执行上传附件操作时,附件组件就会自动自动调用后台的AttachmentManager#create接口来实现附件的上传,接口参数定义如下:

	/**
     * 该方法必须和 comp=type:'fileupload' 配合使用
     * @param category         所属应用分类
     * @param reference        主题Id,如协同的Id
     * @param subReference      二级主题Id,如协同的回复Id,如果当前是给协同上传附件,则该subReference 与 reference相同
     * @return 附件类型任意组合 如:"012"、"12"、"01"、"02"; 其中 0-文件附件 1-图片 2-关联文 ,排列无序
     * @see com.seeyon.ctp.common.filemanager.Constants.ATTACHMENT_TYPE_FILE
     * @see com.seeyon.v3x.common.filemanager.Constants.isUploadLocaleFile(String)
     * @throws Exception
     */
    public String create(ApplicationCategoryEnum category, Long reference, Long subReference) throws Exception;

(3)attsdata后端返回已上传附件列表

前端编写代码comp组件中attsdata='${attachmentsJSON}用于进入JSP页面时,返回并显示已经上传过的附件列表,EL表达式${attachmentsJSON}数据来自于后端,此附件列表数据需要编写在MVC层的Controller层,在重定向到当前JSP页面前准备好数据。

        ModelAndView modelAndView = new ModelAndView("apps/file/inituploadfile");
        // attachmentManager#getAttListJSON(主数据Id)可以组装attsdata数据
        modelAndView.addObject("attachmentsJSON", attachmentManager.getAttListJSON(dataId));
        return modelAndView;

# 附件上传-扩展方式

扩展方式除了由平台完成附件的上传外,还支持前端自定义回调函数:在平台上传附件成功之后将结果返回给回调函数,方便业务组拿到数据做自己的后续逻辑开发。

如下场景:产品支持上传excel格式的通讯录,上传之后,业务组解析通讯录文件并转化为业务组需要的数据,最后持久化到数据库中。

基础以上场景,这个业务实际要进行两段操作:

  • 附件组件:上传通讯录文件

  • 附件组件上传成功后通知业务组回调函数

  • 业务组回调函数拿到上传文件信息,并调用业务组自己的后端接口:后端解析Excel文件,并转化为业务组数据持久化到数据库

  • 业务组后端接口:返回页面,告知用户成功或失败信息。

从上面过程看,从第三步开始与附件上传的标准过程不同,需要调用者来处理。

(1)编写前端代码:

首先,业务组需要在JSP页面准备好一个回调函数:

<script type="text/javascript">
//附件上传后回调方法
function testCallBack(file,d){
  // 业务组处理文件逻辑的动作
  var fileUrl = file.get(0).fileUrl;
  var fileId = file.get(0).id;
  sendAjax(fileUrl,fileId);
}
</script>

然后,参照标准模式,定义附件组建区域,唯一要增加的是:在附件组件comp属性增加callMethod:'testCallBack'参数。

其它附件组件相关comp属性均参照标准模式的属性列表

    <div class="comp" comp="type:'fileupload',callMethod:'testCallBack'">
    <input type="button" onclick="insertAttachment()" value="上传附件">

(2)编写后端代码:

后端存在两种情况:

  1. 不保存附件信息,即获取到上传文件后,获取到文件数据后该文件不再使用,不需要作为附件存储起来。
  2. 保存附件信息。

场景一:业务组不保存附件信息

通过fileManager#getFile可以得到刚上传的文件:

    public ModelAndView index(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView modelAndView = new ModelAndView("apps/file/selfupload");
		// 获取刚上传的文件对象
        File file = fileManager.getFile(Long.parseLong(request.getParameter("fileid"),new Date()));
        //对得到的文件进行处理
        modelAndView.addObject("filename", file.getName());
        modelAndView.addObject("filelength", file.length());
        return modelAndView;
    }

场景二:业务组自己保存附件信息

在后端业务操作中调用如下接口,用来关联附件也业务数据:

 /**
     * 根据文件标识新引用
     * @param fileUrl 文件标识
     * @param referenceId 业务id
     */
    public  void updateReference(Long fileUrl, Long referenceId);

    /**
     * 根据文件标识更新引用及子引用
     * @param fileUrl 文件标识
     * @param referenceId 业务主ID
     * @param subReference 业务子ID
     */
    public  void updateReferenceSubReference(Long fileUrl, Long referenceId, Long subReference);
1 与该业务ID相关的所有附件都在同一处显示,使用该接口更新。
2 与该业务ID相关的所有附件都在同多处显示,每处根据子ID进行区分,使用该接口更新。

# 附件上传-js动态创建方式

如果业务组不需要编写附件组件<div comp=fileupdate>相关代码,只需要用前端Javascript直接操作附件上传组件,则可以使用本方案。

通过调用js方法:dymcCreateFileUpload() 来实现。对于不需要的特性输入null即可。

/**
 * locationElementid, 位置id,放置上传组件的位置,比如div的id("dyncid"):<div id="dyncid"> </div>
 * applicationCategory, 应用模块id
 * extensions, 文件扩展名,逗号分隔的字符串
 * quantity, 最多上传的文件数
 * isEncrypt, 是否加密
 * callMethod, 回调方法名
 * attachmentTrId, 对赢得文件上传的id
 * firstSave,是否先保存附件表信息,默认false:后保存。
 * atts 附件数据
 * canDeleteOriginalAtts 是否可删除附件
 * takeOver 是否接管附件上传(即弹出窗口关闭)之后的逻辑。 该属性和callMethod配合使用,如果不使用回调方法,可以不使用该属性; 否则属性值应为false
 * maxSize 上传文件的大小限制,单位为字节
    */
    function dymcCreateFileUpload(locationElementid,applicationCategory,extensions,quantity,isEncrypt,callMethod, attachmentTrId,firstSave,canDeleteOriginalAtts,atts,takeOver,maxSize){

对于以上方式不能满足交互需要,可以使用下面接口灵活控制附件相关特性。

/**
 * 获取当前组件提交的附件数据。对于一页存在多个上传组件,每个组件所在区域单独处理附件时(即只处理本区域附件,其他区域附件不处理),
 * 使用该方法,可以把该组件上传的附件数据,存放到以组件ID为domainid的区域内。
 * inputDomainId 上传组件id
    */
    function saveAttachmentPart(inputDomainId ){
编撰人:het