jquery File Upload 插件应用之同时递交form field与多个文件

   之前有一文章讨论如何用jquery File Upload(http://blueimp.github.io/jQuery-File-Upload/) 上传文件并取得返回的json对象. http://blog.csdn.net/rocklee/article/details/52759350

   但是这个例子用的是选择了文件后立即上传,如果还需要输入其他form field一起递交的话,后台处理就特别麻烦,这时候必须将自动递交off掉,然后自己手动写代码实现, 我们先看看官网的一个手动递交的例子:http://blueimp.github.io/jQuery-File-Upload/basic-plus.html,这个例子实现了可以选中多个文件,然后逐个手动递交。 虽然它没有演示如何和form field一起递交,但里面的代码可以参考,主要的是参数options里面的autoUpload:要设置为false,然后加上fileuploadadd'事件。

   为了实现这个功能,我写了一个简单的讨论区模块,支持多文件上传,还有一个comment的文件输入框(textarea)。

   首先定义附件table和相应的entity:

CREATE TABLE tl_attachment
(
  fl_attachment_id bigint NOT NULL,
  fl_attachment_name character varying,
  fl_attachment_desc character varying,
  fl_create_by character varying(10) NOT NULL,
  fl_create_dt timestamp without time zone NOT NULL,
  fl_confirm boolean,
  fl_thread_id bigint NOT NULL,
  CONSTRAINT tl_attachment_pkey PRIMARY KEY (fl_attachment_id)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE tl_attachment
  OWNER TO postgres;

-- Index: idx_tl_thread_id

-- DROP INDEX idx_tl_thread_id;

CREATE INDEX idx_tl_thread_id
  ON tl_attachment
  USING btree
  (fl_thread_id);

entity:

package com.freestyle.oms.entities;

import java.io.Serializable;
import javax.persistence.*;
import java.sql.Timestamp;


/**
 * The persistent class for the tl_attachment database table.
 * 
 */
@Entity
@Table(name="tl_attachment")
@NamedQuery(name="TlAttachment.findAll", query="SELECT t FROM TlAttachment t")
public class TlAttachment extends com.freestyle.common.spring.BaseEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	@Id
	@SequenceGenerator(name="TL_ATTACHMENT_FLATTACHMENTID_GENERATOR", sequenceName="TL_ATTACH_ID",allocationSize=1)
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TL_ATTACHMENT_FLATTACHMENTID_GENERATOR")
	@Column(name="fl_attachment_id")
	private Long flAttachmentId;

	@Column(name="fl_attachment_desc")
	private String flAttachmentDesc;

	@Column(name="fl_attachment_name")
	private String flAttachmentName;

	@Column(name="fl_confirm")
	private Boolean flConfirm;

	@Column(name="fl_create_by")
	private String flCreateBy;

	@Column(name="fl_create_dt")
	private Timestamp flCreateDt;
	@Column(name="fl_thread_id")
	private Long flThreadId;

	public Long getFlThreadId() {
		return flThreadId;
	}

	public void setFlThreadId(Long flThreadId) {
		this.flThreadId = flThreadId;
	}

	public TlAttachment() {
	}

	public Long getFlAttachmentId() {
		return this.flAttachmentId;
	}

	public void setFlAttachmentId(Long flAttachmentId) {
		this.flAttachmentId = flAttachmentId;
	}

	public String getFlAttachmentDesc() {
		return this.flAttachmentDesc;
	}

	public void setFlAttachmentDesc(String flAttachmentDesc) {
		this.flAttachmentDesc = flAttachmentDesc;
	}

	public String getFlAttachmentName() {
		return this.flAttachmentName;
	}

	public void setFlAttachmentName(String flAttachmentName) {
		this.flAttachmentName = flAttachmentName;
	}

	public Boolean getFlConfirm() {
		return this.flConfirm;
	}

	public void setFlConfirm(Boolean flConfirm) {
		this.flConfirm = flConfirm;
	}

	public String getFlCreateBy() {
		return this.flCreateBy;
	}

	public void setFlCreateBy(String flCreateBy) {
		this.flCreateBy = flCreateBy;
	}

	public Timestamp getFlCreateDt() {
		return this.flCreateDt;
	}

	public void setFlCreateDt(Timestamp flCreateDt) {
		this.flCreateDt = flCreateDt;
	}

}

后台Controller:

@Resource
	protected ForumModule module;
...
	@AuthPassport
	@RequestMapping("/OMS350/updateComment/{fl_order_id}/{fl_prod_id}")
	public @ResponseBody String updateComment(HttpServletRequest request, HttpServletResponse response,
			@PathVariable("fl_order_id") String pvOrderid,@PathVariable("fl_prod_id") String pvProdId) {
		JSONData lvRet=new JSONData();
		try {
			HashMap<String, FileItem>  lvFiles=new HashMap<String, FileItem> ();
			Map<String,Object>lvParams= HttpBeanUtil.beanFromUrlRequestByJsonV1(HashMap.class, new TypeReference<HashMap>() {  
            }, request, lvFiles,null); //从request获得form fields通过方法返回, 并将附件返回至lvFiles
			String lvComment=(String) lvParams.get("comment");
			module.updateComment(pvOrderid,pvProdId,lvComment,lvFiles, getCurrLogin(request),getLocale(request) );
		} catch (Exception e) {
			BaseModule.jsonDataFillErrMsg(lvRet,e);
		}		
		return JsonUtils.jsonFromObject(lvRet);
	}	
	
...	
ForumModule 的相关方法:

/**** 
     * 上传文件处理 
     * @param pvAppNo 
     * @param pvRev 
     * @param pvFiles 
     * @param pvThreadId thread's id
     * @throws ModifyDataException  
     */  
    private void uploadFiles(String pvOrderId,String pvProdId,Long pvThreadId,HashMap<String, FileItem>pvFiles,String pvLogin,Locale pvLocale) throws ModifyDataException{  
        for (Entry<String, FileItem>item:pvFiles.entrySet()){                    
             if (item.getValue().getSize()>(OmsSetting.c_upload_max_size*1024*1024)){
               	throw new ModifyDataException(MessageSourceHelper.getMessage("MSG35005",new Object[]{OmsSetting.c_upload_max_size},"", pvLocale));
             }  
        }  
        for (Entry<String, FileItem>item:pvFiles.entrySet()){  
            String lvDestPath=OmsSetting.c_appfs_dir+"/"+pvOrderId+"/"+pvProdId;  
            File lvT=new File(lvDestPath);  
            if (!lvT.exists()){  
                lvT.mkdirs();  
            }  
            String lvFileName=String.valueOf(ContextHolder.getJdbcTemplate().queryForObject("select nextval('TL_ATTACH_ID')", Long.class));  
            lvDestPath=lvDestPath+"/"+lvFileName;  
            try{  
                FileUploadModule.saveFileItem(item.getValue(), lvDestPath);  
            }  
            catch (Exception e){  
                throw new ModifyDataException(MessageSourceHelper.getMessage("MSG35004",new Object[]{e.getMessage()},"", pvLocale));  
            }  
            TlAttachment lvNew=new TlAttachment();
            //lvNew.setFlAttachmentId(Long.valueOf(lvFileName));
            lvNew.setFlAttachmentName(lvFileName);  
            lvNew.setFlAttachmentDesc(item.getValue().getName());
            lvNew.setFlThreadId(pvThreadId);
            lvNew.setFlConfirm(Boolean.valueOf(true)); //因为这里文件上传与form field一起,所以直接确认.
            try {  
                setModifyLog(pvLogin, lvNew, false);  
            } catch (Exception e) {  
            }  
            try {  
            	mTlAttaChDao.save(lvNew, true);  
            	//
            } catch (ModifyDataException e) {  
            	mTlAttaChDao.clear();  
                throw new ModifyDataException(e.getMessage());  
            }  
        }  
    }	
	public void updateComment(String pvOrderId,String pvProdId,String pvComment,HashMap<String, FileItem> pvFiles,String pvLogin,Locale pvLocale) throws ModifyDataException{
		if (pvComment.trim().equals("")){
			throw new ModifyDataException(MessageSourceHelper.getMessage("MSG35002", pvLocale));
		}
		try{
			TtComment lvNew=new TtComment();
			lvNew.setFtComment(pvComment);
			lvNew.setFtOrderId(pvOrderId);
			lvNew.setFtProdId(Long.valueOf(pvProdId));
			setModifyLog(pvLogin, lvNew, false);
			mTtCommentDao.save(lvNew, true); //先保存form fields数据于Ttcomment表,
			//save attachments
			uploadFiles(pvOrderId, pvProdId, lvNew.getFtSeq(), pvFiles, pvLogin, pvLocale); //再将附件数据保存于tl_attachment表
			
			List<String> lvUsers=new ArrayList<String>();
			if (!Util.fillNullStr(OmsSetting.forumNotifyList).equals("")){
				
				lvUsers.addAll(Arrays.asList(OmsSetting.forumNotifyList.split(",")));
			}
			Map<String,Object> lvT=ContextHolder.getJdbcTemplate().queryForMap("select fj_salesman_id,fj_salesmanager_id from tj_order_mstr where fj_order_id=?",pvOrderId);
			lvUsers.add((String) lvT.get("fj_salesman_id"));
			lvUsers.add((String) lvT.get("fj_salesmanager_id"));
			TlOrderProdPK lvId=new TlOrderProdPK(pvOrderId,Long.valueOf(pvProdId));
			TlOrderProd lvTl=mTlProdDao.get(lvId);			
			String lvApprList=Util.fillNullStr( ContextHolder.getJdbcTemplate().queryForObject("select fr_appr_by from tr_appr_step where fr_appr_cat=? and fr_seq=? ", new Object[]
					{lvTl.getFlType().equals("B")?"PRODB":"PRODP",lvTl.getFlStatus()}, String.class));
			if (!lvApprList.equals("")){
				lvUsers.addAll(Arrays.asList(lvApprList.split(",")));
			}
			HashSet<String> lvTT=new HashSet<>(lvUsers);
			lvUsers.clear();
			lvUsers.addAll(lvTT);
			lvUsers.remove(pvLogin);
			TjOrderMstr lvTj=mTlProdDao.get(TjOrderMstr.class,pvOrderId);
			ThCust lvTh=mHelper.getCustomerInfoById(lvTj.getFjCustId());
			String lvUserName=getUserName(pvLogin);
			//MSG35001=关于订单"{0}-{1}"的商品"{2}",{3}回复:"{4}"
			String lvContent=MessageSourceHelper.getMessage("MSG35001",new Object[]{lvTj.getFjOrderId(),lvTh.getFhName(),lvTl.getFlName(),lvUserName,pvComment},"", pvLocale);
			mNotify.sendNotify(mTlProdDao.getSession(), Util.toString(lvUsers,","), pvLogin, "", lvContent, "",lvTj.getFjQuotedId());
		}
		catch (Exception e){
			mTtCommentDao.clear();
			throw new ModifyDataException(e.getMessage());
		}
	}	
uploadFiles方法会调用FileUploadModule的saveFileItem方法,将FileItem保存至指定的附件文件夹,代码如下 :

/****
	 * 保存文件
	 * @param pvFileItem
	 * @param pvSaveFilePath
	 * @throws IOException 
	 */
	public static void saveFileItem(FileItem pvFileItem,String pvSaveFilePath) throws IOException{
		InputStream lvIs=pvFileItem.getInputStream();
		FileOutputStream lvOs=new FileOutputStream(new File(pvSaveFilePath));
		byte[] lvBuffer=new byte[2048];
		int lvLen=0;
		while ((lvLen=lvIs.read(lvBuffer))>0){
			lvOs.write(lvBuffer, 0, lvLen);
		}
		lvIs.close();
		lvOs.close();
		pvFileItem.delete();
	}

好了,后台已经准备好,现在看看前端如何实现 。 我们将准备jquery file upload及期依赖,因为jquery file upload 依赖jquery ui ,所以要将jquery ui写在前面 :

<link rel="stylesheet" href="media/ext/jquery-ui.css" />
<script src="media/ext/jquery-ui.js" type="text/javascript"></script>
<script type="text/javascript" src="media/ext/jquery.fileupload.js"></script>
<link rel="stylesheet" href="media/css/jquery.fileupload.css"/>
<link rel="stylesheet" href="media/css/jquery.fileupload-ui.css"/>
前面的html布局:

<!-- detail table start -->
    <%
    List<String> mdFields=Arrays.asList("ft_seq,ft_order_id,ft_prod_it,ft_comment,ft_create_dt,ft_create_dt,fa_name,attachid".split(","));
    %>
        <div class="portlet-body no-more-tables" style="display: block;">
          <h4 class="form-section" style="margin: 0px"><fmt:message key="lab350.last_comment"/></h4>
      <table class="table_list tablesorter table-bordered table-striped table-condensed cf" cellpadding="0"
              cellspacing="0" id="tb_det"
              style='border-width: 0 px; border-color: #CCC;  min-width: 350px;width:98%; '>
        <thead>       
        
        </thead>
        <tbody>
          <tr valign="top" style="display: none" class="cellFormat">
            <td data-title="<fmt:message key="tt_comment.ft_comment"/>" data-column="ft_comment" ></td>               
            <td nowrap="nowrap" data-title="<fmt:message key="tt_comment.ft_create_by"/>" data-column="ft_create_by"></td>
          </tr>
        </tbody>
        <tfoot id="tf_all">
            <form id="frm_det">
           <tr style="vertical-align: middle;">
          <th colspan="2" align="center" 
            style="text-align: center;" valign="middle">
            
            
            <!-- page widget start -->
            <div id="id_page" style="display: inline;margin:5px;vertical-align:middle;float:left"></div>
            <div style="display: inline;margin-top:8px;vertical-align:middle;float:left">         
                <fmt:message key="common.pagesize"></fmt:message>: 
               <select  class="pagesize pageSelect">
                <option value="10" >10</option>
                <option value="20" selected>20</option>
                <option value="50">40</option>
              </select>
              </div>
              <!-- page widget end -->
                <input type="button" style="display: none;"
            class="<bs:style key="normal"/> btnInsert" value="<fmt:message key="common.add"/>"
            style=" float: left;    margin-left: 50px;    margin-top: 5px;" 
            onClick="mActInsert();" />
          </th>
        </tr>
        <tr>
          <th colspan="2" valign="top">
             <div id="upload_progress" style="height: 20px;background-color: #FBDD9F;display: none;vertical-align: middle;text-align: center;"></div>
             <textarea rows="3" id="ed_comment" style="width:80%" placeholder="<fmt:message>lab350.input_hint</fmt:message>"></textarea>
             <button class="<bs:style key="submit"/> >" id="btnPostComment" style="vertical-align: top;"><fmt:message key="common.submit" /></button>
             <fieldset>
             	<legend><fmt:message>lab350.attachment</fmt:message>
             		<!-- 
             		<button class="<bs:style key="normal"/>" id="btnAddAttach"><fmt:message>common.add</fmt:message></button>
             		 -->
             		<span class="btn btn-success fileinput-button">
        					<i class="m-icon-white icon-plus"></i>
        					<span><fmt:message>common.add</fmt:message></span>
        					<!-- The file input field used as target for the file upload widget -->
        					<input id="fileupload" type="file" name="files[]" multiple="">
    				</span>
             	</legend>
             	<div id="files" class="files"></div>
             </fieldset>
             
          </th>
        </tr>
        </form>
        </tfoot>
      </table>        
        </div>
     
  <!-- detail table end -->



前端脚本:

$(document).ready(function(){
	   $("#div_file_panel").find("button").popover();
	   var mvUploadData=null;
	   var lvProgress=$('#upload_progress');
	   $("#btnPostComment").click(function(){//点击Post的动作
		      if (mvUploadData==null||mvUploadData.files.length==0){ //如果没有附件,则直接用jquery的post递交
		    	  postJSON("./OMS350/updateComment/{fl_order_id}/{fl_prod_id}".format(m_cur_edit_data),{comment:$("#ed_comment").val()},function(ret){
		    	  	  if (ret.errCode!=0){
		    	  		  alert(ret.errMsg);
		    	  		  return;
		    	  	  }
		    	  	  m_dedit.fetchData();
		    	  	$("#ed_comment").val("");
		      	   });
		      }
		      else{ //如果有附件,则需要用公用变量myUploadData递交,myUploadData来自第一个附件里面取出的data对象,请考虑下面fileupload的添加文件事件。
		    	  mvUploadData.option("url","./OMS350/updateComment/{fl_order_id}/{fl_prod_id}".format(m_medit.currData)); //option文件是自己修改file upload plugin增加上去的
		    	  mvUploadData.option("formData",{comment:$("#ed_comment").val()});
		    	  lvProgress.show();
		    	  mvUploadData.submit().always(function(){
		    		  mvUploadData=null;
		    		  lvProgress.hide();
		    		  m_dedit.fetchData();
			    	  $("#ed_comment").val("");
			    	  $("#files").children().remove();
		      	  });
		      }
		      return false;
		});  
	   
	   $('#fileupload').fileupload({
	        //url: "./OMS350/updateComment/{fl_order_id}/{fl_prod_id}".format(m_cur_edit_data),
			//formData:{comment:$("#ed_comment").val()},
	        dataType: 'json',
	        type:"post",
	        autoUpload: false,
	        acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
	        maxFileSize: <%=OmsSetting.getC_upload_max_size()*1024*1024%>,
	        // Enable image resizing, except for Android and Opera,
	        // which actually support image resizing, but fail to
	        // send Blob objects via XHR requests:
	        disableImageResize: /Android(?!.*Chrome)|Opera/
	            .test(window.navigator.userAgent),
	        previewMaxWidth: 100,
	        previewMaxHeight: 100,
	        previewCrop: true
	    }).on('fileuploadadd', function (e, data) {
	    	if (mvUploadData==null){
	    		mvUploadData=data;
	    	}
	        data.context = $('<div/>').appendTo('#files');
	        $.each(data.files, function (index, file) {
	            var node = $('<p/>')
	                    .text(file.name).append($('<span><i class="m-icon-white icon-remove"/></span>'));
	            node.find("span").on("click",function(){
	                    	var lvData=$(this).parent().data();
	                    	if (!lvData.files || !lvData.files.length) return;
	                    	var lvIdx=mvUploadData.files.indexOf(lvData.files[0]);
	                    	mvUploadData.files.removeByIndex(lvIdx);
	                    	$(this).parent().remove();
	                    });
	            if (!index) {
	                node
	                    .append('<br>')
	                    .data(data);
	            }
	            node.appendTo(data.context);
	            if (mvUploadData!=data){
	            	mvUploadData.files.push(file);
	            	console.log("add to files:",file);
	            }	            
	            
	        });
	    }).on('fileuploadprocessalways', function (e, data) {	       
	    }).on('fileuploadprogressall', function (e, data) {
	        var progress = parseInt(data.loaded / data.total * 100, 10);
	        lvProgress.css(
	            'width',
	            progress + '%'
	        );
	    }).on('fileuploaddone', function (e, data) {
	    	mvUploadData=null;
	    	var lvRet=null;
	    	if (typeof data.result!="object"){
		    	var lvContent="";  	    	
	            if (typeof data.result!="string"){  
	                lvContent=data.result[0].body.innerText;  
	            }  
	            else{  
	                lvContent=data.result;  
	            }
	            lvRet=eval('('+lvContent+')');
	    	}
	    	else{
	    		lvRet=data.result;	
	    	}
              
            if (lvRet.errCode!=0){  
                alert(lvRet.errMsg);  
                return;  
            }              
	    }).on('fileuploadfail', function (e, data) {	       
	        alert("<fmt:message>MSG35003</fmt:message>");
	    }).prop('disabled', !$.support.fileInput)
	        .parent().addClass($.support.fileInput ? undefined : 'disabled');             
	   
	   
	   
   });

修改jquery.fileupload.js,在data.submit = function () 前面插入如下代码:

data.option=function(key,value){
            	var lvOptions=this.options();
            	lvOptions[key]=value;
            };    
            data.options=function(){
            	return that._getOptions();
            };

在_getAJAXSettings: function (data) {前面插入如下代码:

_getOptions:function(){
        	return this.options;
        },


效果预览:

输入讨论内容, 选择两个附件后:



点击递交后:



全程后台递交、后台刷新 。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值