动态多附件上传
----经典解决方案
动态多附件上传,在目前的B/S系统中经常会遇到这方面的需求,基于开源的理念,在此我把几个解决方案做详细的介绍。目的在于提高自己的描述问题的能力,同时给遇到同样问题的朋友提供一些线索,以便节省宝贵时间。
上次项目是做一个**管理系统,其中有一个模块是作品发布,作品发布模块就需要实现这样一个功能:
新建一个作品,作品包含一些基本的信息,而且要求要有一个或多个附件,每个附件又要求有作品概要(即作品附件和作品概要一一对应)。
系统要求用struts来实现。
在讨论解决方案之前,首先要了解的是。在客户端请求服务器时提交表单,表单的内容只能为String型和FormFile型(FormFile类型只有在Struts中才有),不能提交自己定义的dto对象。但是可以提交自己定义的dto中的String成员和FormFile成员。
了解了这个原理之后,我开始阐述第一种解决方案;
第一种解决方案:间接动态多附件上传
基本思路:定义一个ProductForm类
Class ProductForm extends ActionForm{
String pro_name;
……. //其他一些基本的成员变量;
//生成get和set方法;
}
在jsp页面上用相关的标签定义作品的字段;
如:<html:text property=”pro_name” />
动态多附件则用普通标签实现,没有在ProductForm中定义相应的字段。因此,Action取值也不能直接从ActionForm中取值,因为ActionForm中根本没有相应的成员。
这里给出相关代码:
Jsp部分:
<TABLE id='mytable'> <tr><td> </td> </tr>
</TABLE>
<html:button value="添加新附件" styleClass="btn" onclick="add_Row(mytable);" property="add_newFile" />
动态添加附件域在jsp中的代码是:
var i = 0;
function add_Row(the_table) { //动态增加一行,实际上也就是增加一个表格
var the_row,the_cell;
var cur_rows=the_table.rows.length;
the_row=cur_rows==null?-1:(cur_rows);
// var i=the_row;
var newrow=the_table.insertRow(i);//得到插入位置
newrow.id = i;
the_cell=newrow.insertCell(0);
the_cell.innerHTML="<table width='359' border='0' cellspacing='0' id='table'>"
+ "<tr>"
+ "<td width='300'><input id='<%= Constants.RES_NEW_FILE_PARAMETER_NAME%>"+i+"' name='<%= Constants.RES_NEW_FILE_PARAMETER_NAME%>"+i+"' type='file' οnchange='fileTypeCheck(this);' οnpaste='return false' οnkeypress='return false' οnkeydοwn='return false' οncοntextmenu='return false;'></td>"
+ "<td width='55'><input name='deleteRow' type='button' class='btn' value='删除此域' οnclick='del("+i+");'></td>"
+ "</tr>"
+ "<tr>"
+ "<td ><span class='useBB'>文件概要说明[<=50汉字]</span></td>"
+ "<td class='style6'> </td>"
+ "</tr>"
+ "<tr>"
+ "<td>"
+ "<textarea name='"+i+"' class='new_files_comnt' οnchange='checkLength(this,100);'></textarea></td>"
+ "<td align='left' valign='top'> "
+ "</td>"
+ "</tr>"
+ "</table><hr>";
i++;}
其中<input id='<%= Constants.RES_NEW_FILE_PARAMETER_NAME%>"+i+"' name='<%= Constants.RES_NEW_FILE_PARAMETER_NAME%>"+i+"' type='file' οnchange='fileTypeCheck(this);' οnpaste='return false' οnkeypress='return false' οnkeydοwn='return false' οncοntextmenu='return false;'>和<textarea name='"+i+"' class='new_files_comnt' οnchange='checkLength(this,100);'></textarea>
中的Constants.RES_NEW_FILE_PARAMETER_NAME是在常量类Constants.java中定义的常量,每个Struts项目中都会有这个类文件,目的在于统一接口。
你可能会注意到name='<%= Constants.RES_NEW_FILE_PARAMETER_NAME%>"+i+"'和name='"+i+"'中都有i这个变量,没错,这就是为了能让“附件”和“附件概要”能一一绑定,取到一个“附件”之后,马上根据后缀i就可以取得其相应的“附件概要”。
所以在Action层取作品的相关信息如下:
1. 新建一个作品Dto,Product product = new Product();//Product是自己定义的Dto对象。
2. 封装作品基本信息,如:product.setPro_name = ProductForm.getPro_name();
3. 间接取附件信息;
有关代码如下:
public ActionForward newProductIn(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response){
ProductForm productForm=(ProductForm)form;
//封装作品基本信息
product=new Product ();
product.setPro_name(productForm.getPro_name().trim()); //作品名称
…………………… //还有很多其他字段;
//开始封装所有的附件和附件概要信息;
Hashtable allfiles= productForm.getMultipartRequestHandler()
.getFileElements();
Hashtable texts=productForm.getMultipartRequestHandler()
.getTextElements();
for(int i = 0;i<allfiles.size();i++){ //得到所有附件域的值和其相对应的附件概要的值
String[] fileComnt = (String[])productForm.getMultipartRequestHandler().
getTextElements().get(Integer.toString(i));
FormFilefileCotent = (FormFile)productForm.getMultipartRequestHandler().
getFileElements().get(Constants.RES_NEW_FILE_PARAMETER_NAME+i);
if(fileCotent.getFileName().equals("")){
continue;
}
else{//取附件信息和附件概要信息进行封装
productFileInfor=new ProductFileInfor();(productFileInfor为自己定义Dto,用来存储附件信息)
InputStream in=fileCotent.getInputStream();
productFileInfor.setInput_file_stream(in);//设置输入流 productFileInfor.setFile_comment(fileComnt[0]);
}
以上为本人自己设计的多附件上传方案。
第二种方案:直接动态多附件上传
这个方案为借鉴网上已有设计,但原创者的设计在实施上存在一些错误,因此本人把其中设计进行改进,同时实现第一种方案的业务需求。
首先:ActionForm设计
public class MultiUploadForm extends ActionForm {
/**
*
*/
private static final long serialVersionUID = 1L;
private List myFiles;
@SuppressWarnings("unchecked")
public MultiUploadForm(){
myFiles = new ArrayList();
//为了能够在页面初始显示一个file
myFiles.add(new UploadFile());
}
public List getMyFiles() {
return myFiles;
}
//注意这个方法的定义,非常重要,实现动态上传完全在于这个函数的定义
@SuppressWarnings("unchecked")
public UploadFile getUploadFile(int index){
int size = myFiles.size();
if (index > size-1) {
for (int i=0;i<index-size+1;i++){
myFiles.add(new UploadFile());
}
}
return (UploadFile)myFiles.get(index);
}
public void setMyFiles(List myFiles) {
this.myFiles = myFiles;
}
Dto定义:
public class UploadFile implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private FormFile file;
String file_comment;
public String getFile_comment() {
return file_comment;
}
public void setFile_comment(String file_comment) {
this.file_comment = file_comment;
}
public FormFile getFile() {
System.out.println("run in uploadFile.getFile()");
return file;
}
public void setFile(FormFile file) {
this.file = file;
}
}
Jsp代码:
<%@ page language="java" pageEncoding="UTF-8"
import = "com.icss.multifile.web.form.MultiUploadForm"
%>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>
<%@ taglib uri="http://struts.apache.org/tags-logic" prefix="logic"%>
<%@ taglib uri="http://struts.apache.org/tags-tiles" prefix="tiles"%>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html"%>
<%@ taglib uri="/WEB-INF/struts-nested.tld" prefix="nested"%>
<html:html>
<head>
<title>
multiUploadDemo
</title>
<SCRIPT language="javascript" charset="">
var num = 0;
function add_Row(the_table) { //动态增加一行,实际上也就是增加一个表格
var the_row,the_cell;
var cur_rows=the_table.rows.length;
the_row=cur_rows==null?-1:(cur_rows);
// var i=the_row;
var newrow=the_table.insertRow(num);//得到插入位置
newrow.id =num; ;
<% MultiUploadForm multiUploadForm=(MultiUploadForm)request.getAttribute("multiUploadForm");
int size = 1;
if(multiUploadForm!=null && multiUploadForm.getMyFiles()!=null && multiUploadForm.getMyFiles().size()>0 ){
size = multiUploadForm.getMyFiles().size();
}
%>
the_cell=newrow.insertCell(0);
the_cell.innerHTML="<table width='359' border='0' cellspacing='0' id='table'>"
+ "<tr>"
+ "<td width='300'><input type=file name=uploadFile[<%= size%>].file value=''></td>"
+ "</tr>"
+ "<tr>"
+ "<td ><span class='useBB'>文件概要说明[<=50汉字]</span></td>"
+ "<td class='style6'> </td>"
+ "</tr>"
+ "<tr>"
+ "<td>"
+ "<textarea name=uploadFile[<%= size%>].file_comment class='new_files_comnt' ></textarea></td>"
+ "<td align='left' valign='top'> "
+ "</td>"
+ "</tr>"
+ "</table><hr>";
num++;
}
function upload(){
multiUploadForm.submit();
}
</SCRIPT>
</head>
<body bgcolor="#ffffff">
<html:form method="post" action="/multiUploadAction.do" enctype="multipart/form-data">
<TABLE id='mytable'>
<logic:iterate id="uploadFile" property="myFiles" name="multiUploadForm" indexId="index">
<tr><td>
<html:file property="file" name="uploadFile" indexed="true" />
<html:textarea property="file_comment" name="uploadFile" indexed="true" ></html:textarea>
</td>
</tr>
</logic:iterate>
</TABLE>
<table border="1">
<TBODY>
<br>
<br>
this is file
<TR>
<input type="button" name="btnAddFile" value="Add File" onclick="add_Row(mytable)" />
<TR>
<TD id="tdTarget"></TD>
<input type="button" name="btnUpload" value="upload" onclick="upload()">
</TBODY>
</table>
</html:form>
</body>
</html:html>
Action代码:
public class MultiUploadAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response) {
MultiUploadForm multiUploadForm = (MultiUploadForm) form;
List myFiles = multiUploadForm.getMyFiles();
for(int i =0;i<myFiles.size();i++){
UploadFile uploadFile = (UploadFile)myFiles.get(i);
FormFile file = uploadFile.getFile();
if(file==null){
System.out.println("file is null");
}
else{
//能运行到这里,就可以使用单个文件上传的方法进行上传了,做循环;
System.out.println("filename========" + file.getFileName());
System.out.println("file size=======" + file.getFileSize());
System.out.println("file Comment=======" + uploadFile.getFile_comment());
}
}
return mapping.findForward("success");
}
}
上面两种方法比较起来,第二种方案的代码聚合度占领上风,目前,还是非常欣赏 这个
方案,它完全利用了Struts的结构设计优势,因此在验证和取值上都非常方便。因
此,
在这里特意和大家共享。