Struts 2中实现文件上传

前一阵子有些朋友在电子邮件中问关于Struts 2实现文件上传的问题, 所以今天我们就来讨论一下这个问题。

实现原理

Struts 2是通过Commons FileUpload文件上传。Commons FileUpload通过将HTTP的数据保存到临时文件夹,然后Struts使用fileUpload拦截器将文件绑定到Action的实例中。从而我们就能够以本地文件方式的操作浏览器上传的文件。

具体实现

前段时间Apache发布了Struts 2.0.6 GA,所以本文的实现是以该版本的Struts作为框架的。以下是例子所依赖类包的列表:

依赖类包的列表 
清单1 依赖类包的列表

首先,创建文件上传页面FileUpload.jsp,内容如下:

<% @ page language = " java " contentType = " text/html; charset=utf-8 " pageEncoding = " utf-8 " %>
<% @ taglib prefix = " s " uri = " /struts-tags " %>

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
   
< title > Struts 2 File Upload </ title >
</ head >
< body >
   
< s:form action ="fileUpload" method ="POST" enctype ="multipart/form-data" >
       
< s:file name ="myFile" label ="Image File" />
       
< s:textfield name ="caption" label ="Caption" />        
       
< s:submit />
   
</ s:form >
</ body >
</ html >

清单2 FileUpload.jsp

在FileUpload.jsp中,先将表单的提交方式设为POST,然后将enctype设为multipart/form-data,这并没有什么特别之处。接下来,<s:file/>标志将文件上传控件绑定到Action的myFile属性。

其次是FileUploadAction.java代码:

package tutorial;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;

import org.apache.struts2.ServletActionContext;

import com.opensymphony.xwork2.ActionSupport;

public class FileUploadAction extends ActionSupport {
   
private static final long serialVersionUID = 572146812454l ;
   
private static final int BUFFER_SIZE = 16 * 1024 ;
   
   
private File myFile;
   
private String contentType;
   
private String fileName;
   
private String imageFileName;
   
private String caption;
   
   
public void setMyFileContentType(String contentType) {
       
this .contentType = contentType;
   }

   
   
public void setMyFileFileName(String fileName) {
       
this .fileName = fileName;
   }

       
   
public void setMyFile(File myFile) {
       
this .myFile = myFile;
   }

   
   
public String getImageFileName() {
       
return imageFileName;
   }

   
   
public String getCaption() {
       
return caption;
   }


   
public void setCaption(String caption) {
       
this .caption = caption;
   }

   
   
private static void copy(File src, File dst) {
       
try {
           InputStream in
= null ;
           OutputStream out
= null ;
           
try {                
               in
= new BufferedInputStream( new FileInputStream(src), BUFFER_SIZE);
               out
= new BufferedOutputStream( new FileOutputStream(dst), BUFFER_SIZE);
               
byte [] buffer = new byte [BUFFER_SIZE];
               
while (in.read(buffer) > 0 ) {
                   out.write(buffer);
               }

           }
finally {
               
if ( null != in) {
                   in.close();
               }

               
if ( null != out) {
                   out.close();
               }

           }

       }
catch (Exception e) {
           e.printStackTrace();
       }

   }

   
   
private static String getExtention(String fileName) {
       
int pos = fileName.lastIndexOf( " . " );
       
return fileName.substring(pos);
   }


   @Override
   
public String execute()     {        
       imageFileName
= new Date().getTime() + getExtention(fileName);
       File imageFile
= new File(ServletActionContext.getServletContext().getRealPath( " /UploadImages " ) + " / " + imageFileName);
       copy(myFile, imageFile);
       
return SUCCESS;
   }

   
}

清单3 tutorial/FileUploadAction.java

在FileUploadAction中我分别写了setMyFileContentType、setMyFileFileName、setMyFile和setCaption四个Setter方法,后两者很容易明白,分别对应FileUpload.jsp中的<s:file/>和<s:textfield/>标志。但是前两者并没有显式地与任何的页面标志绑定,那么它们的值又是从何而来的呢?其实,<s:file/>标志不仅仅是绑定到myFile,还有myFileContentType(上传文件的MIME类型)和myFileFileName(上传文件的文件名,该文件名不包括文件的路径)。因此,<s:file name="xxx" />对应Action类里面的xxx、xxxContentType和xxxFileName三个属性。

FileUploadAction作用是将浏览器上传的文件拷贝到WEB应用程序的UploadImages文件夹下,新文件的名称是由系统时间与上传文件的后缀组成,该名称将被赋给imageFileName属性,以便上传成功的跳转页面使用。

下面我们就来看看上传成功的页面:

<% @ page language = " java " contentType = " text/html; charset=utf-8 " pageEncoding = " utf-8 " %>
<% @ taglib prefix = " s " uri = " /struts-tags " %>

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns ="http://www.w3.org/1999/xhtml" >
< head >
   
< title > Struts 2 File Upload </ title >
</ head >
< body >
   
< div style ="padding: 3px; border: solid 1px #cccccc; text-align: center" >
       
< img src ='UploadImages/<s:property value ="imageFileName" /> ' />
       
< br />
       
< s:property value ="caption" />
   
</ div >
</ body >
</ html >

清单4 ShowUpload.jsp

ShowUpload.jsp获得imageFileName,将其UploadImages组成URL,从而将上传的图像显示出来。

然后是Action的配置文件:

<? xml version="1.0" encoding="UTF-8" ?>

<! DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd"
>

< struts >
   
< package name ="fileUploadDemo" extends ="struts-default" >
       
< action name ="fileUpload" class ="tutorial.FileUploadAction" >
           
< interceptor-ref name ="fileUploadStack" />
           
< result name ="success" > /ShowUpload.jsp </ result >
       
</ action >
   
</ package >
</ struts >

清单5 struts.xml

fileUpload Action显式地应用fileUploadStack的拦截器。

最后是web.xml配置文件:

<? xml version="1.0" encoding="UTF-8" ?>
< web-app id ="WebApp_9" version ="2.4"
    xmlns
="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >

   
< display-name > Struts 2 Fileupload </ display-name >

   
< filter >
       
< filter-name > struts-cleanup </ filter-name >
       
< filter-class >
            org.apache.struts2.dispatcher.ActionContextCleanUp
       
</ filter-class >
   
</ filter >
    
   
< filter >
       
< filter-name > struts2 </ filter-name >
       
< filter-class >
            org.apache.struts2.dispatcher.FilterDispatcher
       
</ filter-class >
   
</ filter >
    
   
< filter-mapping >
       
< filter-name > struts-cleanup </ filter-name >
       
< url-pattern > /* </ url-pattern >
   
</ filter-mapping >

   
< filter-mapping >
       
< filter-name > struts2 </ filter-name >
       
< url-pattern > /* </ url-pattern >
   
</ filter-mapping >

   
< welcome-file-list >
       
< welcome-file > index.html </ welcome-file >
   
</ welcome-file-list >

</ web-app >

清单6 WEB-INF/web.xml

发布运行应用程序,在浏览器地址栏中键入:http://localhost:8080/Struts2_Fileupload/FileUpload.jsp,出现图示页面:


清单7 FileUpload页面

选择图片文件,填写Caption并按下Submit按钮提交,出现图示页面:


清单8 上传成功页面

更多配置

在运行上述例子,如果您留心一点的话,应该会发现服务器控制台有如下输出:

Mar 20 , 2007 4 : 08 : 43 PM org.apache.struts2.dispatcher.Dispatcher getSaveDir
INFO: Unable to find 'struts.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir
Mar
20 , 2007 4 : 08 : 43 PM org.apache.struts2.interceptor.FileUploadInterceptor intercept
INFO: Removing file myFile C:/Program Files/Tomcat
5.5 /work/Catalina/localhost/Struts2_Fileupload/upload_251447c2_1116e355841__7ff7_00000006.tmp

清单9 服务器控制台输出

上述信息告诉我们,struts.multipart.saveDir没有配置。struts.multipart.saveDir用于指定存放临时文件的文件夹,该配置写在struts.properties文件中。例如,如果在struts.properties文件加入如下代码:

struts.multipart.saveDir = /tmp

清单10 struts配置

这样上传的文件就会临时保存到你根目录下的tmp文件夹中(一般为c:/tmp),如果此文件夹不存在,Struts 2会自动创建一个。

错误处理

上述例子实现的图片上传的功能,所以应该阻止用户上传非图片类型的文件。在Struts 2中如何实现这点呢?其实这也很简单,对上述例子作如下修改即可。

首先修改FileUpload.jsp,在<body>与<s:form>之间加入“<s:fielderror />”,用于在页面上输出错误信息。

然后修改struts.xml文件,将Action fileUpload的定义改为如下所示:

        < action name ="fileUpload" class ="tutorial.FileUploadAction" >
           
< interceptor-ref name ="fileUpload" >
               
< param name ="allowedTypes" >
                    image/bmp,image/png,image/gif,image/jpeg
               
</ param >
           
</ interceptor-ref >
           
< interceptor-ref name ="defaultStack" />            
           
< result name ="input" > /FileUpload.jsp </ result >
           
< result name ="success" > /ShowUpload.jsp </ result >
       
</ action >

清单11 修改后的配置文件

显而易见,起作用就是fileUpload拦截器的allowTypes参数。另外,配置还引入defaultStack它会帮我们添加验证等功能,所以在出错之后会跳转到名称为“input”的结果,也即是FileUpload.jsp。

发布运行应用程序,出错时,页面如下图所示:


清单12 出错提示页面

上面的出错提示是Struts 2默认的,大多数情况下,我们都需要自定义和国际化这些信息。通过在全局的国际资源文件中加入“struts.messages.error.content.type.not.allowed=The file you uploaded is not a image”,可以实现以上提及的需求。对此有疑问的朋友可以参考我之前的文章《在Struts 2.0中国际化(i18n)您的应用程序》。

实现之后的出错页面如下图所示:


清单13 自定义出错提示页面

同样的做法,你可以使用参数“maximumSize”来限制上传文件的大小,它对应的字符资源名为:“struts.messages.error.file.too.large”。

字符资源“struts.messages.error.uploading”用提示一般的上传出错信息。

多文件上传

与单文件上传相似,Struts 2实现多文件上传也很简单。你可以将多个<s:file />绑定Action的数组或列表。如下例所示。

< s:form action ="doMultipleUploadUsingList" method ="POST" enctype ="multipart/form-data" >
   
< s:file label ="File (1)" name ="upload" />
   
< s:file label ="File (2)" name ="upload" />
   
< s:file label ="FIle (3)" name ="upload" />
   
< s:submit />
</ s:form >

清单14 多文件上传JSP代码片段

如果你希望绑定到数组,Action的代码应类似:

    private File[] uploads;
   
private String[] uploadFileNames;
   
private String[] uploadContentTypes;

   
public File[] getUpload() { return this .uploads; }
   
public void setUpload(File[] upload) { this .uploads = upload; }

   
public String[] getUploadFileName() { return this .uploadFileNames; }
   
public void setUploadFileName(String[] uploadFileName) { this .uploadFileNames = uploadFileName; }

   
public String[] getUploadContentType() { return this .uploadContentTypes; }
   
public void setUploadContentType(String[] uploadContentType) { this .uploadContentTypes = uploadContentType; }

清单15 多文件上传数组绑定Action代码片段

如果你想绑定到列表,则应类似:

    private List < File > uploads = new ArrayList < File > ();
   
private List < String > uploadFileNames = new ArrayList < String > ();
   
private List < String > uploadContentTypes = new ArrayList < String > ();

   
public List < File > getUpload() {
       
return this .uploads;
   }

   
public void setUpload(List < File > uploads) {
       
this .uploads = uploads;
   }


   
public List < String > getUploadFileName() {
       
return this .uploadFileNames;
   }

   
public void setUploadFileName(List < String > uploadFileNames) {
       
this .uploadFileNames = uploadFileNames;
   }


   
public List < String > getUploadContentType() {
       
return this .uploadContentTypes;
   }

   
public void setUploadContentType(List < String > contentTypes) {
       
this .uploadContentTypes = contentTypes;
   }

清单16 多文件上传列表绑定Action代码片段

总结

在Struts 2中实现文件上传的确是轻而易举,您要做的只是使用<s:file />与Action的属性绑定。这又一次有力地证明了Struts 2的简单易用。

 

http://www.blogjava.net/max/archive/2007/03/21/105124.html

 

 

 

 

struts2文件上传与下载(3)

struts学习   2009-07-20 10:06   阅读158   评论0   字号: 大大  中中  小小     这里,我们终于要用strust2自带的文件上传系统来做我们自己的文件上传了....

    首先,当然是写页面了..

upload.jsp  这个在webroot下面的upload文件夹下...没啥可说的.就是一个html

<body>

 <form action="upload.action" method="post" enctype="multipart/form-data">
 
 Information: <input type="text" name="info"><br>
 File: <input type="file" name="file"><br>
 
 <input type="submit" name="submit" value=" submit ">
 
 </form>

</body>

result.jsp 这里记得加strust2标签引入...

<body><br>

info:<s:property value="info"/><br>
filename:<s:property value="fileFileName"/>


</body>

2 写action的java文件 upload.java

这里比较长.我一个个解释

package com.test.action;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import com.opensymphony.xwork2.ActionSupport;

public class upload extends ActionSupport {
 
 private String info;
 private File file ; // 这里对应的jsp页面上的两个字段.没啥可以说.
 private String fileFileName;//注意,这个表示的是file这个上传对象的文件名..这个是struts2自己封装的.可以自动获取,不用和上一节一样需要我们自己去写代码.. 这里的名字后面部分"FileName"是固定的,前面的file就是上面那个file.也就是说和jsp页面的那个字段的name是对应的..
 private String fileContentType;//这里也同样,表示文件类型.struts2已经封装好的.后面的"ContentType"也是固定的.
 public String getInfo() { //下面大部分都是get,set方法,没啥好说的.都是自动生成的.
  return info;
 }
 public void setInfo(String info) {
  this.info = info;
 }
 public File getFile() {
  return file;
 }
 public void setFile(File file) {
  this.file = file;
 }
 public String getFileFileName() {
  return fileFileName;
 }
 public void setFileFileName(String fileFileName) {
  this.fileFileName = fileFileName;
 }
 public String getFileContentType() {
  return fileContentType;
 }
 public void setFileContentType(String fileContentType) {
  this.fileContentType = fileContentType;
 }
 
 @Override
 public String execute() throws Exception {
  //重头戏从这里开始
  InputStream is = new FileInputStream(file); //定义一个输入流..file对应上面的上传文件对象.
  String root = "D://file";//这里先定义一个文件上传的目录...就是将上传的文件放到D盘的file文件下面.
  File diskfile  = new File(root,this.getFileFileName());//定义输出流存放的文件夹..文件名为getFileFileName .也就是说,是和上传的文件相同.
  OutputStream os = new FileOutputStream(diskfile);//定义一个输出流..对应输出的文件夹
 
 
  byte[] buffer = new byte[400];//这里和之前的上传与下载第一节里的内容一样.不多解释了..
  int length = 0;
  while((length = is.read(buffer))>0)
  {
   os.write(buffer);
  }
  is.close();
  os.close();
 
  return SUCCESS;
 }

}

3.最后就是strut.xml配置文件了

注意,我为了避免干扰,讲拦截器都注释掉了...

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

 <constant name="struts.i18n.encoding" value="gbk"></constant>//这里定义一个编码方式是用gbk.这样你传文件的时候就可以用中文的文件名.要不然就出乱码
 
 <constant value="d:/"></constant>//这里定义的是临时目录..文件上传的时候会先将文件复制到临时目录,然后将临时目录里的文件用IO的方式上传.
 
 <constant value="message"></constant>
 
 <package name="struts2" extends="struts-default">

  <interceptors>
   <interceptor name="myInterceptor" class="com.test.interceptor2.MyInterceptor">
    <param name="hello">world</param>
   </interceptor>
  
   <interceptor name="myInterceptor2" class="com.test.interceptor2.MyInterceptor2">
   </interceptor>
  
   <interceptor name="myInterceptor3" class="com.test.interceptor2.MyInterceptor3">
  
   </interceptor>
  
   <interceptor-stack name="myStack">
  
   <!--
    <interceptor-ref name="myInterceptor"></interceptor-ref>
    <interceptor-ref name="myInterceptor2"></interceptor-ref>
   
    -->
    
    <interceptor-ref name="defaultStack"></interceptor-ref>
   
   </interceptor-stack>
  </interceptors>

  <default-interceptor-ref name="myStack"></default-interceptor-ref>

  <action name="login" class="com.test.action.LoginAction">
   <result name="input">/login2.jsp</result>
   <result name="success">/result.jsp</result>
   <result name="failer">/login2.jsp</result>
  </action>
 
  <action name="pointConverter" class="com.test.action.PointAction">
   <result name="success">/output.jsp</result>
   <result name="input">/input.jsp</result>
  </action>
 
  <action name="upload" class="com.test.action.upload">
   <result>/upload/result.jsp</result>
  </action>
 
  <action name="register" class="com.test.action.RegisterAction" method="test">
   <result name="success">/success.jsp</result>
   <result name="input">/register2.jsp</result>
  <!--  
   <interceptor-ref name="myInterceptor3">
    <param name="excludeMethods">test,execute</param>
    <param name="includeMethods">test</param>
   </interceptor-ref>
   <interceptor-ref name="defaultStack"></interceptor-ref>
   -->
  </action>
 
 </package>

</struts>
其他没可以说的.这样,我们的文件上传就已经实现了..

注意,要注入common-io.jar和common-fileupload.jar..

 

 

你可能感兴趣的:(Struts 2中实现文件上传)