说到文件上传,最开始想到的用JSPSmartUpload完成图片上传。后来放弃,因为它适用于普通动态网页,而我的网站是s2sh的。查到用struts2的文件上传最适合,以下就是开发经验。
首先,导入相应的包,我用的是struts2的标签<s:form>,其中特别重要的两个属性method="post" enctype="multipart/form-data"。里面是<s:file name="pic">,再加一个<s:submit value="上传"/>,文件上传输入页面完成。
该页面通过struts.xml文件的设置,对应到相应的Action类,类里面有
private File pic;
private String picFileName;
和其get和set方法。File pic对应<s:file name="pic">,而String picFileName并没有在输入页面设置,它从何而来呢?
原来它是系统默认并自动传递的参数,只需把File的name加上“FileName”,String picFileName里就会有值,在Action类就可以使用。好神奇啊。
接收上传文件数据的代码,可以在Action类中定义,以下是代码:
public String add() throws Exception{
DateFormat format=new SimpleDateFormat("yyyyMMddHHmmss");
String formatdate=format.format(new Date());
int random=new Random().nextInt(10000);
int position=picFileName.lastIndexOf(".");
String extension=picFileName.substring(position);
String newfilename=formatdate+random+extension;
String realPath=ServletActionContext.getServletContext().getRealPath("images/product");
File saveFile=new File(realPath,newfilename);
FileUtils.copyFile(pic, saveFile);
product.setPic("images/product/"+newfilename);
productServiceImpl.add(product);
return SUCCESS;}
通过System.out.print(picFileName);显示了当前上传的文件名,可是马上又提示错误,说File pic的值为null。嗯?这是怎么回事?文件名都可以传递,文件本身却为空。
难到要在struts.xml里设置Fileupload的interceptor拦截器,研究下来,不需要。
在success.jsp里写${pic},显示有上传文件tmp存在。而这个tmp文件在Action里却“收不到”。说明这个传递被“某些东西”阻挡了。
经过不停的调试,发现问题竟然是Action类的接口:ModelDriven。去除此接口以及它的getModel()方法,File pic里就有值了,大功告成。
上传的文件大小要在2M以内,类型可以是图片,MP3,txt等。
把这些写下来,勉励自己,提示他人。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
我说过,文件上传功能与struts2的ModelDriven是冲突的。
有ModelDriven接口的action会拦截File pic对象,导致上传出错。
我现在要做一个product_updateInput.jsp顾名思义,这是产品更新的导入页。
要把action类中的product对象传到product_updateInput.jsp的文件中,再把product的字段导入文本域。
我之前做过把product_addInput.jsp新建产品输入页中的数据传到product_add.action中,并数据库保存成功。
即jsp传递数据到action成功,但action传递数据到jsp,尚在研究中。
我的代码是:
public String updateInput() throws Exception{
ActionContext.getContext().put("cs", categoryServiceImpl.listAllCategory());
Product tp=productServiceImpl.load(id);
product.setName(tp.getName());//*
return SUCCESS;
}
运行后,*号行报错,报空指针异常。action无法传递数据到jsp。
public String add() throws Exception{
DateFormat format=new SimpleDateFormat("yyyyMMddHHmmss");
String formatdate=format.format(new Date());
int random=new Random().nextInt(10000);
int position=picFileName.lastIndexOf(".");
String extension=picFileName.substring(position);
String newfilename=formatdate+random+extension;
String realPath=ServletActionContext.getServletContext().getRealPath("images/product");
File saveFile=new File(realPath,newfilename);
FileUtils.copyFile(pic, saveFile);//*
product.setPic("images/product/"+newfilename);
productServiceImpl.add(product,cid);
//System.out.print(product);
return SUCCESS;
}
无奈,只好加了ModelDriven接口和getModel()方法,传输数据成功。但同时,文件上传功能*号行开始报错。
另说一句,只有加了ModelDriven接口,product对象才会出现在值栈的顶端(值栈的底端是ProductAction)。
于是我又在“ModelDriven接口”和“文件上传功能”上纠结,出现了鱼和熊掌不能兼得的情况。
我开始想解决办法。
即然是FileUtils.copyFile(pic, saveFile);出问题,我就改另一种上传方法。
public String add() throws Exception{
DateFormat format=new SimpleDateFormat("yyyyMMddHHmmss");
String formatdate=format.format(new Date());
int random=new Random().nextInt(10000);
int position=picFileName.lastIndexOf(".");
String extension=picFileName.substring(position);
String newfilename=formatdate+random+extension;
String realPath=ServletActionContext.getServletContext().getRealPath("images/product");
BufferedOutputStream bos=null;
BufferedInputStream bis=null;
try{
FileInputStream fis=new FileInputStream(pic);
bis=new BufferedInputStream(fis);
FileOutputStream fos=new FileOutputStream(new File(realPath,newfilename));//*
bos=new BufferedOutputStream(fos);
byte[] buf=new byte[1024];
int len=-1;
while((len=bis.read(buf))!=-1){
bos.write(buf,0,len);
}
}finally{
try{
if(bis!=null){
bis.close();
}
}catch(IOException e){
e.printStackTrace();
}
try{
if(bos!=null){
bos.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
product.setPic("images/product/"+newfilename);
productServiceImpl.add(product,cid);
//System.out.print(product);
return SUCCESS;
}
可是,*号行报错,这个方法也不行。
我想让文件上传功能,避开ModelDriven接口,于是我把文件上传代码写到Service类的方法中,再由继承ModelDriven接口的Action类“调用”。
但是没用,因为Action类接收不到File pic,调用失败。
我想自己写个拦截器,拦截Action类使其通过文件上传,避开ModelDriven,仔细想想,这不可能,于是也放弃了。
我想研究避开ModelDriven接口,实现action传递数据到jsp,才是可行的。
action里传参数,只有实行<result type="redirectAction">才能使用<parem>参数,但还要指定一个"actionName",这是不符合我的项目设定,所以不可用。
我在struts.xml加了
<action name="product_updateInput" class="productAction" method="updateInput">
<result>/admin/product_updateInput.jsp?product=${product}</result>
</action>
即在文件名?后“传递”参数,结果么,果然是失败的。
最后,我在updateInput()方法里,写了System.out.println(product);得到的是null,我很吃惊。
我一直以为,Action 类中已经有了从jsp文件传来的product类,product类是包含数据的,有“内容”的类。
如果product类是null的话。我单单写product.setName(tp.getName());这句代码,是会出“问题”的。于是我加了一句……
product=new Product();
完整代码为
public String updateInput() throws Exception{
ActionContext.getContext().put("cs", categoryServiceImpl.listAllCategory());//使商品类型下拉框显示数据库里的值<s:select list="#cs" listKey="id" listValue="name" name="cid" />
Product tp=productServiceImpl.load(id);//从数据库中导入数据
product=new Product();
product.setName(tp.getName());//使jsp的文本域显示数据库中的数据
return SUCCESS;
}
我的天,Action传递数据到jsp成功!<input type="text" name="product.name" value="${product.name}"/>