之前有一文章讨论如何用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;
},
效果预览:
输入讨论内容, 选择两个附件后:
点击递交后:
全程后台递交、后台刷新 。