`

《研磨struts2》第十三章 文件上传下载 之 13.1 文件上传

 
阅读更多

 

 

13.1  文件上传

13.1.1  概述

在开发基于Web的应用的时候,经常会碰到需要实现文件上传、下载的功能,比如编辑某个商品,需要给它上传一个图片等等。

不使用Struts2的话,可以有很多种方式来实现文件上传,比如使用Apache的Common-FileUpload等。但是这些传统的方式,实现起来非常麻烦,需要写很多代码来进行控制。

现在使用Struts2来实现文件上传的功能,会更加简单和方便,事实上,Struts2的文件上传功能,默认就是基于Common-FileUpload来实现的,只不过比直接使用Common-FileUpload来得更简单。

13.1.2使用Struts2实现文件上传

1:fileUpload拦截器

       使用Struts2来实现文件上传,需要使用Struts2预定义的fileUpload拦截器。简要的看看在struts-default.xml中的配置,示例如下:

 

java代码:
  1. <package name="struts-default" abstract="true">  
  2.     …  
  3.     <interceptors>  
  4.         …  
  5.         <interceptor name="fileUpload"  
  6. ss="org.apache.struts2.interceptor.FileUploadInterceptor"/>  
  7.         …  
  8.         <interceptor-stack name="defaultStack">  
  9.             …  
  10.             <interceptor-ref name="fileUpload"/>  
  11.             …  
  12.         </interceptor-stack>  
  13.    </interceptors>  
  14.   
  15.    <default-interceptor-ref name="defaultStack"/>  
  16. </package>  

上面列出了struts-default.xml中跟fileUpload拦截器有关的部分。首先,Struts2有一个预定义拦截器叫fileUpload;然后,defaultStack拦截器栈引用了fileUpload拦截器;最后,struts-default包又将defaultStack拦截器声明为自己的默认拦截器引用。

因此,如果我们的包继承了struts-default包,而我们的Action类又没有声明自己的拦截器引用的话,使用的就是defaultStack拦截器栈,从而引用了fileUpload拦截器。

2:实现上传页面

       如果一个表单中,包含了文件上传的表单域,则一定要把整个表单enctype属性设置为multipart/form-data。形如:

 

java代码:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  4.  "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  8. <title>Insert title here</title>  
  9. </head>  
  10. <body>  
  11. <form action="/helloworld/uploadAction.action" method="post"   
  12.     enctype="multipart/form-data">  
  13.     文件名称:<input type="text" name="fileName"><br>  
  14.     文件:<input type="file" name="myFile"><br>  
  15.     <input type="submit" value="提交">  
  16. </form>  
  17. </body>  
  18. </html>  

在这个文件里,指定了<form>元素的enctype属性为multipart/form-data,还用了一个type为file的输入域,它会在页面上生成一个文件选择框。整个页面运行如下:

图13.1 文件上传页面

在这个页面上文件域里面,在输入框后面多了一个“浏览”按钮,点击这个浏览按钮,就会弹出文件选择框。

3:实现文件上传Action

       这个Action和其他的Action没有太多特殊之处,使用一个String属性fileName来准备接收前一个页面的fileName文本框的值,然后使用一个File属性myFile来准备接收前一个页面的myFile文件框传过来的文件的值。

在execute方法中,我们可以随意存储上传的文件,本例只是把它放到了e盘的temp目录下,并以传入的fileName作为保存的文件名。

示例代码如下:

 

java代码:
  1. public class UploadAction extends ActionSupport{  
  2.     private String fileName;  
  3.     private File myFile;  
  4.       
  5.     public String getFileName() {  
  6.         return fileName;  
  7.     }  
  8.     public void setFileName(String fileName) {  
  9.         this.fileName = fileName;  
  10.     }  
  11.     public File getMyFile() {  
  12.         return myFile;  
  13.     }  
  14.     public void setMyFile(File myFile) {  
  15.         this.myFile = myFile;  
  16.     }  
  17.       
  18.     public String execute()throws Exception{  
  19.         //先把上传过来的文件存放到e盘temp目录下,以传入的fileName为名字  
  20.         OutputStream output = null;  
  21.         InputStream input = null;  
  22.         try{  
  23.             output = new FileOutputStream("e:/temp/"+fileName);  
  24.             //建立一个1k大小的缓冲区  
  25.             byte[] bs = new byte[1024];  
  26.               
  27.             //将上传过来的文件输出到output中  
  28.             input = new FileInputStream(myFile);  
  29.             int length = 0;  
  30.             //length=input.read(bs)这句话中,length=-1代表了读到文件结尾  
  31.             while ((length=input.read(bs))>0){  
  32.                 output.write(bs, 0, length);  
  33.             }  
  34.         }finally{  
  35.             input.close();  
  36.             output.close();  
  37.         }         
  38.         return SUCCESS;  
  39.     }  
  40. }  

注意:如何操作上传的文件,完全是I/O的知识,为了保持实例的简单,只是采用了最简单的方式,把内容存放在一个绝对路径表示的文件里面,当然还可以将上传的文件存储在web服务器上,甚至数据库中。

4:映射这个Action

       在struts.xml中对这个Action进行配置,可以看到没有任何特异之处,只是指出了Action名称和对应的实现类,以及上传之后要跳转到的页面。

 

java代码:
  1. <package name="helloworld" extends="struts-default">  
  2.     <action name="uploadAction" class="cn.javass.fileupload.UploadAction">  
  3.         <result>/fileupload/success.jsp</result>  
  4.     </action>  
  5. </package>  

5:上传之后的页面

       由于上传的文件在Action中已经被正确处理了,所以这个上传之后的页面没有任何特殊的作用,只是显示一下,表示上传成功了,示例代码如下:

 

java代码:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <%@taglib prefix="s" uri="/struts-tags" %>  
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  5.  "http://www.w3.org/TR/html4/loose.dtd">  
  6. <html>  
  7. <head>  
  8. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  9. <title>Insert title here</title>  
  10. </head>  
  11. <body>  
  12.     成功上传文件<s:property value="fileName"/>  
  13. </body>  
  14. </html>  

运行测试一下,在文件上传页面选择要上传的文件,然后点击提交按钮,会发现文件会上传,并以我们指定的文件名称保存到我们指定的文件夹下去了。

13.1.3获取文件的更多信息

Struts2支持用更多的属性来获取有关上传文件的真实文件名和文件类型。

比如:表单中包含了一个叫xyz的文件域,也就是类似于<input type=”file” name=”xyz”,那么可以用三个属性来获取上传文件的信息:

  • File类型的属性xyz用来获取文件内容。
  • String类型的属性xyzFileName用来获取文件的真实文件名。
  • String类型的属性xyzContentType属性用来获取文件的类型。

将处理上传文件的Action类修改一下,添加两个属性:myFileFileName和myFileContentType,以及他们对应的getter/setter方法,来尝试获得文件的真实文件名和文件类型。

另外,保存文件的时候,也使用文件的真实名称,示例代码如下:

 

java代码:
  1. public class UploadAction extends ActionSupport{  
  2.     private String fileName;  
  3.     private File myFile;  
  4.     private String myFileFileName;  
  5.     private String myFileContentType;  
  6.     public String getMyFileFileName() {  
  7.         return myFileFileName;  
  8.     }  
  9.     public void setMyFileFileName(String myFileFileName) {  
  10.         this.myFileFileName = myFileFileName;  
  11.     }  
  12.     public String getMyFileContentType() {  
  13.         return myFileContentType;  
  14.     }  
  15.     public void setMyFileContentType(String myFileContentType) {  
  16.         this.myFileContentType = myFileContentType;  
  17.     }     
  18.     public String getFileName() {  
  19.         return fileName;  
  20.     }  
  21.     public void setFileName(String fileName) {  
  22.         this.fileName = fileName;  
  23.     }  
  24.     public File getMyFile() {  
  25.         return myFile;  
  26.     }  
  27.     public void setMyFile(File myFile) {  
  28.         this.myFile = myFile;  
  29.     }  
  30.       
  31.     public String execute()throws Exception{  
  32.         System.out.println("用户传入的文件名:"+fileName);  
  33.         System.out.println("上传文件的真实文件名:"+myFileFileName);  
  34.         System.out.println("上传文件的类型:"+myFileContentType);  
  35.         System.out.println("这个文件的文件名是什么?"+myFile.getName());  
  36.   
  37.         //把上传过来的文件存放到e盘temp目录下,以真实的文件名作为名字  
  38.         OutputStream output = null;  
  39.         InputStream input = null;  
  40.         try{  
  41.             output = new FileOutputStream("e:/temp/"+myFileFileName);  
  42.             //建立一个1k大小的缓冲区  
  43.             byte[] bs = new byte[1024];  
  44.               
  45.             //将上传过来的文件输出到output中  
  46.             input = new FileInputStream(myFile);  
  47.             int length = 0;  
  48.             //length=input.read(bs)这句话中,length=-1代表了读到文件结尾  
  49.             while ((length=input.read(bs))>0){  
  50.                 output.write(bs, 0, length);  
  51.             }  
  52.         }finally{  
  53.             input.close();  
  54.             output.close();  
  55.         }         
  56.         return SUCCESS;  
  57.     }  
  58. }  

运行测试一下,后台输出为:

 

java代码:
  1. 用户传入的文件名:aaa  
  2. 上传文件的真实文件名:filelist.xml  
  3. 上传文件的类型:text/xml  
  4. 这个文件的文件名是什么?upload__5ab75acc_12c5f17ac7a__8000_00000001.tmp  

注意其中“这个文件的文件名是什么?”,代码写的是输出myFile这个File对象的名字,由于myFile对象封装了上传文件的内容,因而某些朋友就认为这个文件就是我们上传的文件,从而认为它的getName方法就是返回的上传的这个文件的名字。

这是错误的,这个myFile文件是Struts2在上传过程中生成的临时文件,它的内容与上传文件相同,但是名字并不相同,它的名字是由Struts2临时生成的,形如“upload__5ab75acc_12c5f17ac7a__8000_00000001.tmp”这样的。

13.1.4限制文件的大小及类型

在文件上传的时候,有可能需要对文件的大小和类型做出限制。Struts2支持直接在fileUpload拦截器上设置参数来进行限制。

       在引用fileUpload拦截器的时候,可以指定三个参数(指定<param>子元素):

  • allowedTypes:指定允许上传的文件的类型,如果存在多种类型,以逗号隔开。注意:这里添的不是文件的扩展名,而是对应的ContentType,如果不知道某种文件的ContentType可以先上传一下试试,在后台输出ContentType来。
  • maximumSize:指定允许上传的文件的最大字节数。
  • allowedExtensions:指定允许上传的文件的扩展名。

如果上传的文件不满足以上的参数指定的条件,则会跳转到一个叫input的<result>上,一般input都会指回到提交之前的页面,也就是文件上传页面。

参数非常简单,但是配置比较怪异,fileUpload拦截器虽然在defaultStack拦截器栈上已经引用了,但是还可以在defaultStack拦截器栈之前再引用一次,然后在这次引用上指定参数,示例代码如下:

 

java代码:
  1. <package name="helloworld" extends="struts-default">  
  2.     <action name="uploadAction" class="cn.javass.fileupload.UploadAction">  
  3.         <interceptor-ref name="fileUpload">  
  4.             <param name="allowedTypes">text/plain</param>  
  5.             <param name="maximumSize">1000</param>  
  6.         </interceptor-ref>  
  7.         <interceptor-ref name="defaultStack"/>  
  8.           
  9.         <result name="input">/fileupload/fileupload.jsp</result>  
  10.         <result>/fileupload/success.jsp</result>  
  11.     </action>  
  12. </package>  

观察以上的<action>元素,要额外注意两点:

  • 这个Action应该先引用fileUpload拦截器,然后引用defaultStack拦截器栈。
  • 这个Action中还要有一个叫input的<result>,以备上传不成功时,Struts2跳转到提交前的页面。

还要稍微修改一下login.jsp,来显示不满足限制条件时的错误信息。只需要在合适的地方加上<s:fielderror/>来显示错误信息即可。形如:

 

java代码:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  4.  "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  8. <title>Insert title here</title>  
  9. </head>  
  10. <body>  
  11. <%@taglib prefix="s" uri="/struts-tags" %>  
  12. <s:fielderror/>  
  13. <form action="/helloworld/uploadAction.action" method="post"   
  14.     enctype="multipart/form-data">  
  15.     文件名称:<input type="text" name="fileName"><br>  
  16.     文件:<input type="file" name="myFile"><br>  
  17.     <input type="submit" value="提交">  
  18. </form>  
  19. </body>  
  20. </html>  

运行修改之后的文件上传功能,如果上传非txt文件,运行结果如下:

图13.2 fileUpload拦截器限制文件上传类型

       如果上传大于1000字节的txt文件,运行结果如下:

图13.3 fileUpload拦截器限制文件上传大小

13.1.5上传超大的文件

Struts2在实现文件上传的时候,还有一个小问题,那就是默认上传文件的大小是不能超过2097152字节的。这个配置在struts2-core-2.1.8.1.jar文件里面,“\org\apache\struts2”文件夹下的default.properties文件里面,配置如下:

 

java代码:
  1. struts.multipart.maxSize=2097152  

先来测试一下,看看是不是有这个问题。到文件上传页面,选择一个超过上述字节数的文件来上传看看,点击提交按钮,后台输出错误,示例如下:

 

java代码:
  1. org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (3402567) exceeds the configured maximum (2097152)  
  2.     at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:914)  
  3.     ......  

注意上面加粗的那句话,明确告诉我们,上传的文件大小为3402567,大于配置的最大值2097152字节。

       那么该怎么办呢?

       方法很简单,只要覆盖这个默认设置就可以了,有两种方式,一种是在Struts.properties里面配置,另外一种就是在struts.xml里面配置。这里就以在struts.xml来配置为例,只要在struts.xml中加入如下的常量定义,示例代码如下:

 

java代码:
  1. <constant name="struts.multipart.maxSize" value="10000000" />  

设置上传文件的最大值为大约10M。再次测试运行看看,应该就可以正常上传了。

13.1.6在一个表单中上传多个文件

在实际开发中,经常会碰到需要在一个表单中上传多个文件的情况,比如为某文档添加附件,就可能要求在一个表单上添加多个附件。

对于在一个表单中上传多个文件的功能,使用Struts2也能轻松实现。只需要在提交页面上添加同名的多个文件输入域,然后在Action中对应使用File类型的数组去接收这些参数即可。

       比如上面的示例,可以把上传文件页面改为:

 

java代码:
  1. <%@ page language="java" contentType="text/html; charset=gb2312"  
  2.     pageEncoding="gb2312"%>  
  3. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  
  4.  "http://www.w3.org/TR/html4/loose.dtd">  
  5. <html>  
  6. <head>  
  7. <meta http-equiv="Content-Type" content="text/html; charset=gb2312">  
  8. <title>Insert title here</title>  
  9. </head>  
  10. <body>  
  11. <%@taglib prefix="s" uri="/struts-tags" %>  
  12. <s:fielderror/>  
  13. <form action="/helloworld/uploadAction.action" method="post"   
  14.     enctype="multipart/form-data">  
  15.     文件:<input type="file" name="myFile"><br>  
  16.     文件:<input type="file" name="myFile"><br>  
  17.     文件:<input type="file" name="myFile"><br>  
  18.     文件:<input type="file" name="myFile"><br>  
  19.     <input type="submit" value="提交">  
  20. </form>  
  21. </body>  
  22. </html>  

上例中,<form>表单中有多个同名的file输入域myFile。

       在Action中,只要使用数组或List来操作就可以了,这里以数组为例来修改Action,示例代码如下:

 

java代码:
  1. public class UploadAction extends ActionSupport{  
  2.     private File[] myFile;  
  3.     private String[] myFileFileName;  
  4.     public File[] getMyFile() {  
  5.         return myFile;  
  6.     }  
  7.     public void setMyFile(File[] myFile) {  
  8.         this.myFile = myFile;  
  9.     }  
  10.     public String[] getMyFileFileName() {  
  11.         return myFileFileName;  
  12.     }  
  13.     public void setMyFileFileName(String[] myFileFileName) {  
  14.         this.myFileFileName = myFileFileName;  
  15.     }  
  16.       
  17.     public String execute()throws Exception{  
  18.         //把上传过来的文件存放到e盘temp目录下,以真实的文件名作为名字  
  19.         OutputStream output = null;  
  20.         InputStream input = null;  
  21.         try{  
  22.             for(int i=0;i<myFileFileName.length;i++){  
  23.                 output = new FileOutputStream("e:/temp/"+myFileFileName[i]);  
  24.                 //建立一个1k大小的缓冲区  
  25.                 byte[] bs = new byte[1024];  
  26.                   
  27.                 //将上传过来的文件输出到output中  
  28.                 input = new FileInputStream(myFile[i]);  
  29.                 int length = 0;  
  30.                 //length=input.read(bs)这句话中,length=-1代表了读到文件结尾  
  31.                 while ((length=input.read(bs))>0){  
  32.                     output.write(bs, 0, length);  
  33.                 }  
  34.             }  
  35.         }finally{  
  36.             input.close();  
  37.             output.close();  
  38.         }         
  39.         return SUCCESS;  
  40.     }  
  41. }  

然后就可以去运行测试了,看看上面的实现是不是真的能同时上传多个文件。但是要提醒一点,如果想要上传不同类型的文件,记得把前面示例的时候,在struts.xml中所作的限制设定去掉。

 

私塾在线网站原创《研磨struts2》系列

转自请注明出处:【http://sishuok.com/forum/blogPost/list/0/4121.html

欢迎访问http://sishuok.com获取更多内容

9
6
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics