现在的位置: 首页 > 综合 > 正文

Struts2+Spring+Hibernate上传文件

2013年03月26日 ⁄ 综合 ⁄ 共 14484字 ⁄ 字号 评论关闭

前段时间,我用struts2.1.6、spring2.5、hibernate3.3做了一个文件上传的demo。上传是通过struts2自带的组件Commons FileUpload文件上传的。上传需要的jar包:commons-fileupload-1.2.1.jar和commons-io-1.3.2.jar。 

首先了解一下文件上传的原理: 

表单元素的enctype属性指定的是表单数据的编码方式,该属性有3个值: 

a 、   application/x-www-form-urlencoded:这是默认编码方式,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式。 

b 、   multipart/form-data:这种编码方式的表单会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数里。 

c、     text/plain:这种方式主要适用于直接通过表单发送邮件的方式。 

上传文件主要是通过读写二进制流进行操作的。 

配置很简单,相信网上有很多这方面的资料,我就不再叙述了(最后面引用了网友的对struts2文件上传的配置)。 

主要说明1点:因为spring的bean 默认是单态singleton 
的。这样导致一个问题:就是当我们如果上传了一个不允许的类型文件或超出大小范围的文件,之后再上传正常的文件也上传不了。这时候需要把bean设置为非单态模式:

Java代码 复制代码

  1. <!-- 上传action -->     
  2.     <bean id="uploadAction" class="com.action.upload.UploadAction" scope="prototype">   
  3.      <property name="busiWlphotodirService" ref="busiWlphotodirService"></property>   
  4.     </bean>  

Java代码 复制代码

  1. <!-- 上传action -->     
  2.     <bean id="uploadAction" class="com.action.upload.UploadAction" scope="prototype">   
  3.      <property name="busiWlphotodirService" ref="busiWlphotodirService"></property>   
  4.     </bean>  

<!-- 上传action --> <bean id="uploadAction" class="com.action.upload.UploadAction" scope="prototype"> <property name="busiWlphotodirService"
ref="busiWlphotodirService"></property> </bean>


1.    文件上传的原理: 
表单元素的enctype属性指定的是表单数据的编码方式,该属性有3个值: 
1)      application/x-www-form-urlencoded:这是默认编码方式,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式。 
2)      multipart/form-data:这种编码方式的表单会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数里。 
3)      text/plain:这种方式主要适用于直接通过表单发送邮件的方式。 
文件上传是web应用经常用到的一个知识。原理是,通过为表单元素设置enctype="multipart/form-data"属性,让表单提交的数据以二进制编码的方式提交,在接收此请求的Servlet中用二进制流来获取内容,就可以取得上传文件的内容,从而实现文件的上传。 
在Java领域中,有两个常用的文件上传项目:一个是Apache组织Jakarta的Common-FileUpload组件(http://commons.apache.org/fileupload/),另一个是Oreilly组织的COS框架(http://www.servlets.com/cos/)。利用这两个框架都能很方便的实现文件的上传。 
2.    Struts2的文件上传: 
Struts2并未提供自己的请求解析器,也就是就Struts2不会自己去处理multipart/form-data的请求,它需要调用其他请求解析器,将HTTP请求中的表单域解析出来。但Struts2在原有的上传解析器基础上做了进一步封装,更进一步简化了文件上传。 
Struts2默认使用的是Jakarta的Common-FileUpload框架来上传文件,因此,要在web应用中增加两个Jar文件:commons- fileupload-1.2.jar和commons-io-1.3.1.jar。它在原上传框架上做了进一步封装,简化了文件上传的代码实现,取消了不同上传框架上的编程差异。 
如果要改成其它的文件上传框架,可以修改struts.multipart.parser常量的值为cos/pell,默认值是jakata。并在classpath中增加相应上传组件的类库。 
2.1.    步骤一:创建带上传表单域的页面 
<%@ page language="java" contentType="text/html; charset=UTF-8"%> 
<html> 
<head> 
    <title>Struts2 File Upload</title> 
</head> 
<body> 
    <form action="fileUpload.action" method="POST" enctype="multipart/form-data"> 
        文件标题:<input type="text" name="title" size="50"/><br/> 
        选择文件:<input type="file" name="upload" size="50"/><br/> 
       <input type="submit" value=" 上传 "/>       
    </form> 
</body> 
</html> 

此页面特殊之处只是把表单的enctype属性设置为multipart/form-data。 
2.2.    步骤二:创建处理上传请求的Action类 
package org.qiujy.web.struts2; 
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 org.apache.struts2.ServletActionContext; 
import com.opensymphony.xwork2.ActionSupport; 
/** 
*处理文件上传的Action类 
*@authorqiujy 
*@version1.0 
*/ 
publicclass FileUploadAction extends ActionSupport { 
    privatestaticfinalintBUFFER_SIZE = 16 * 1024; 
    // 文件标题 
    private String title; 
    // 上传文件域对象 
    private File upload; 
    // 上传文件名 
    private String uploadFileName; 
    // 上传文件类型 
    private String uploadContentType; 
    // 保存文件的目录路径(通过依赖注入) 
    private String savePath; 
    //以下省略getter和setter...... 
    //自己封装的一个把源文件对象复制成目标文件对象 
    privatestaticvoid copy(File src, File dst) { 
        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 = newbyte[BUFFER_SIZE]; 
            int len = 0; 
            while ((len = in.read(buffer)) > 0) { 
                out.write(buffer, 0, len); 
            } 
        } catch (Exception e) { 
            e.printStackTrace(); 
        } finally { 
            if (null != in) { 
                try { 
                    in.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
            if (null != out) { 
                try { 
                    out.close(); 
                } catch (IOException e) { 
                    e.printStackTrace(); 
                } 
            } 
        } 
    } 
    @Override 
    public String execute() throws Exception { 
        //根据服务器的文件保存地址和原文件名创建目录文件全路径 
        String dstPath = ServletActionContext.getServletContext() 
                                .getRealPath(this.getSavePath()) 
                                + " 
" + this.getUploadFileName(); 
       
        System.out.println("上传的文件的类型:"+ this.getUploadContentType()); 
       
        File dstFile = new File(dstPath); 
        copy(this.upload, dstFile); 
        returnSUCCESS; 
    } 
{color:black}} | 
上面这个Action类中,提供了title和upload两个属性来分别对应页面的两个表单域属性,用来封装表单域的请求参数。 
但是,值得注意的是,此Action中还有两个属性:uploadFileName和uploadContentType,这两个属性分别用于封装上传文件的文件名、文件类型。这是Struts2设计的独到之处:Strut2的Action类直接通过File类型属性直接封装了上传文件的文件内容,但这个File属性无法获取上传文件的文件名和文件类型,所以Struts2就直接将文件域中包含的上传文件名和文件类型的信息封装到uploadFileName和uploadContentType属性中,也就是说Struts2针对表单中名为xxx的文件域,在对应的Action类中使用3个属性来封装该文件域信息: 
l 类型为File的xxx属性:用来封装页面文件域对应的文件内容。 
l 类型为String的xxxFileName属性:用来封装该文件域对应的文件的文件名。 
l 类型为String的xxxContentType属性:用来封装该文件域应用的文件的文件类型。 
另外,在这个Action类中还有一个savePath属性,它的值是通过配置文件来动态设置的,这也是Strut2设计中的一个依赖注入特性的使用。 
2.3.    步骤三:配置 
struts.xml文件: 
| <!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 ="org.qiujy.web.struts2.FileUploadAction"> 
<!-- 动态设置Action中的savePath属性的值 --> 
            <param name="savePath">/upload</param> 
            <result name ="success">/showupload.jsp</result> 
        </action > 
    </package > 
</struts> 

       在这个文件中跟以前配置唯一不同的是给action配置了一个<param .../>元素,用来为该Action的savePath属性动态注入值。 
web.xml中的配置跟以前的应用一样。说明一点:好多网络文章说Struts2上传时要在web.xml中配置一个名为ActionContextUp的过滤器,说是有一些莫名的错误,可是是我用了Struts2新版本2.0.9GA版,测了n次,没出现什么问题,所以没配置。 
2.4.    运行调试: 
运行前要在根目录下创建一个名为upload的文件夹,用来存放上传后的文件。 
上传结果: 
3.    文件类型及错误输出: 
Struts2提供了一个文件上传的拦截器(名为fileUpload),通过配置这个拦截器能轻松地实现文件类型的过滤。 
在上例中,若要配置上传的文件只能是一些普通的图片文件格式:image/bmp、image/png、image/gif、image/jpeg、image/jpg等,则可在struts.xml文件中按如下方式配置: 
<!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.custom.i18n.resources" value="messages"/> 
    <package name="fileUploadDemo" extends="struts-default"> 
        <action name="fileUpload" 
            class="org.qiujy.web.struts2.FileUploadAction"> 
            <interceptor-ref name="fileUpload"> 
              <!-- 配置允许上传的文件类型,多个用","分隔 --> 
              <param name="allowedTypes"> 
*          *image/bmp,image/png,image/gif,image/jpeg,image/jpg ,image/x-png, image/pjpeg 
              </param> 
              <!-- 配置允许上传的文件大小,单位字节 --> 
              <param name="maximumSize">102400</param> 
           </interceptor-ref> 
           <interceptor-ref name="defaultStack" /> 
            <!-- 动态设置Action中的savePath属性的值 --> 
            <param name="savePath">/upload</param> 
            <result name="input">/index.jsp</result> 
            <result name="success">/showupload.jsp</result> 
        </action> 
    </package> 
</struts> 

如果上传文件失败,系统返回到input对应的页面,要在input对应的页面输出文件过滤失败信息,可以在input对应的页面中增加 <s:fielderror/>来显示错误信息。 
运行调试: 
结果: 
         显然,这样的提示不太友好,应用使用国际化信息。在国际化资源文件中添加如下三句: 
#更改上传文件类型不允许的提示信息 
struts.messages.error.content.type.not.allowed=文件上传失败:你要上传的文件类型不允许 
#更改上传文件太大的提示信息 
struts.messages.error.file.too.large=文件上传失败:你要上传的文件太大 
#文件上传其它错误信息 
struts.messages.error.uploading=文件上传失败:发生内部错误 
以上是我在网上找的一个比较详细的Struts2文件上传的解说但在实际中还要注意一些问题 

1.如果用到了spring的方法注入的话一定不要忘了加 scope="prototype"这样每次访问action时,都会新建一个action,否则的话,会记录上传action留下的记录,出现意想不到的错误。 

2.上传时出现的错误信息是放在request范围内的如果出现错误返回的input页面不是上传文件的form页面那么可以把错误信息提取出来 

String error = this.getFieldErrors().get("file").toString();(file指struts2 file标签里 name的名字) 

放在request,session。。范围内然后在input指定的页面显示错误信息。 

3.文件大小和类型的限制是通过Struts2拦截器实现的你只需要在上传的action中做如下配置就可以 

<param name="allowedTypes"> 
      application/octet-stream,application/x-zip-compressed,image/bmp,image/png,image/gif,image/jpeg,image/jpg,image/x-png, 
      image/pjpeg 
</param> 
<param name="maximumSize">5242880</param> 

但是有一点要注意Struts2默认文件上传最大为2M即便你设置了<param name="maximumSize">5242880</param> 

当上传的文件大于2M时候也会出错的这时要设置另外一个常量<constant name="struts.multipart.maxSize" value="1000000000"/> 

要让他的value设置的比你限定上传最大值要大一点。 

以上是文件上传要注意的几点其实文件上传看起来比较难实际上比较简单,而文件下载就有点麻烦了遇到了很恼人的编码和乱码问题。 

1.文件下载的action和普通的action查不多仅仅是该Action需要提供一个返回InputStream流的方法 

public InputStream getInputStream() throws Exception 
{   
Attachment attachment=attachmentManager.get(Long.valueOf(attachmentId));   
File downFile=new File(attachment.getFilePath());   InputStream inStream = new FileInputStream(downFile);   
return inStream; 

有一点要注意struts2提供了一个 ServletActionContext.getServletContext().getResourceAsStream(filePath);方法根据路径返回输入流,但是这个方法和servletContext有关,当你把下载的文件放在项目的根目录中才可以否则会出错的,最保险的办法就是根据文件路径找到这个文件然后在转化成输入流返回。 

配置: 

<action name="download" class="attachmentAction" 
   method="downloadAttachment"> 
   <result name="success" type="stream"> 
    <param name="bufferSize">2048</param> //输入流缓冲的大小 
    <param name="inputPath">inputStream</param> //inputStream和getInputStream() 是对应的 
   </result> 
</action> 

2.编码,当文件名是中文时 

String downFileName = new String(downFileName.getBytes("ISO8859-1"), "utf-8"); 

getResponse().setHeader( 
    "Content-Disposition", 
    "attachment; filename=" 
      + java.net.URLEncoder.encode(downFileName, "UTF-8")); 

setHeader是设置http请求的头是MIME 协议的拓展可以实现以下功能 

*希望某类或者某已知MIME 类型的文件(比如:.gif;.txt;*.htm)能够在访问时弹出"文件下载"对话框 
*希望以原始文件名(上传时的文件名,例如:山东省政府1024号文件.doc)提供下载,但服务器上保存的地址却是其他文件名(如:12519810948091234_asdf.doc) 
*希望某文件直接在浏览器上显示而不是弹出文件下载对话框 上面的代码实现了第一个功能。

================================================================================

struts+hibernate+spring做图片上传

 (2009-12-16 16:50:35)

hibernate映射文件:
xml 代码
  1. <class name="com.yourcompany.vo.Fileload" table="FILELOAD" schema="TEST">  
  2.         <id name="id" type="integer">  
  3.             <column name="ID" precision="22" scale="0" />  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <property name="fileload" type="org.springframework.orm.hibernate3.support.BlobByteArrayType">  
  7.             <column name="FILELOAD" />  
  8.         </property>  
  9.         <property name="filename" type="string">  
  10.             <column name="FILENAME" length="50" />  
  11.         </property>  
  12.     </class>  

spring配置文件

xml 代码
  1.   <bean id="nativeJdbcExtractor" class="org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor" lazy-init="true">  
  2.    bean>  
  3. <bean id="lobHandler" class="org.springframework.jdbc.support.lob.OracleLobHandler" lazy-init="true">  
  4.   <property name="nativeJdbcExtractor">  
  5.    <ref local="nativeJdbcExtractor" />  
  6.   property>  
  7. bean>  
将lobHandle注入到sessionFactory中
xml 代码
  1. <bean id="mysessionFactory"  
  2.         class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  3.         <property name="dataSource">  
  4.             <ref bean="dataSource" />  
  5.         property>  
  6.         <property name="lobHandler">  
  7.          <ref local="lobHandler"/>  
  8.         property>    
  9.           
在使用struts+spring+hibernate的开发中,有些时候用户会有数据库存储文件的需求,在数据库中一般会采用
Blob字段或Clob字段来存储二进制图片、流媒体或文件。现就将在实际开发中遇到的问题及解决方法告之。
一、问题需求:
 1.在持久化类中字段该用什么类型?
 2.在Struts中文件对应的数据类型是什么?
 3.在Hibernate中字段对应的类型是什么?
 4.如何在Spring中处理这些文件?
二、解决方法:
 1.在数据库中这些文件最终是以字节的形式存储二进制对象,所以在持久化类中将相应的属性定义为byte[]类型。
 2.在Struts中可以使用<html:file property="" />标签来上传文件,所以与之对应的ActionForm的属性类型为:
FormFile(org.apache.struts.upload.FormFile)类型,即使是使用了动态Form同理也是使用该类型.
提示:同时要将form标签修改为<html:form action="/Action.do" method="post" enctype="multipart/form-data">
以支持文件上传后取得上传数据。在给持久对象赋值时只需调用(FormFile)form.get("property")).getFileData();
方法即可返回byte[]。
 3.在Hibernate中文件列对应的类型可以是org.springframework.orm.hibernate3.support.BlobByteArrayType,
org.springframework.orm.hibernate3.support.ClobStringType、clob、blob、binary。什么时候用BlobBYteArrayType
什么时候用ClobStringType?一般如果要处理的对象是数据库字段类型是blob时(主要是图片,二进制对象等),
映射文件设置为:org.springframework.orm.hibernate3.support.BlobByteArrayType 数据库字段是clob(大文本对象)
类型时,要将java的属性的类型定为String,映射文件设置为:org.springframework.orm.hibernate3.support.ClobStringType。
 4.如果在spring上要使用Struts中自带的上传功能必须在spring的配置文件中加以声明.否者将会出现?
簀ava.lang.IllegalStateException: No LobHandler found for configuration - lobHandler property
must be set on LocalSessionFactoryBean异常。配置如下:
 (1).声明一个处理句柄:
  <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true" />
 (2).在sessionFactory中注入lobHandler:
  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
          <property name="lobHandler" ref="lobHandler"/>
  </bean>
 提示: 指定lobHandler时,对于MySQL、DB2、MS SQL Server、Oracle 10g,使用DefaultLobHandler即可,而Oracle 9i,
则可以使用OracleLobHandler。因为Oracle9i处理lob的方式和不太一样,所以这里要用spring提供的SimpleNativeJdbcExtractor.处理Oracle9i lob类型的特殊声明:
<bean id="nativeJdbcExtractor" lazy-init="true" class="org.springframework.jdbc.support.nativejdbc.
SimpleNativeJdbcExtractor"/>
<bean id="lobHandler" lazy-init="true"  class="org.springframework.jdbc.support.lob.OracleLobHandler">
        <property name="nativeJdbcExtractor">
            <ref bean="nativeJdbcExtractor"/>
        </property>
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
  
 <!-- 为处理Blob类型字段的句柄声明 -->
 <property name="lobHandler">
  <ref local="lobHandler" />
 </property>
</bean>
同时还应该使用对应的JDBC驱动。
Clob字段定义:<property name="属性名" column="列名" type="org.springframework.orm.hibernate.support.ClobStringType"
 length="1048"/>这里的length是指字节,最大可以到2G.该字段在java对象中声明为String类型。
     如果使用的是mysql数据库其默认的上传文件大小只有1047552字节:如果上传的文件大于1047552字节则会出现异常
org.springframework.jdbc.UncategorizedSQLException: You can change this value on the server by setting
the max_allowed_packet' variable.根据提示可以修改mysql数据库的max_allowed_packet属性大小。
打开my.ini文件中找到# SERVER SECTION后在port=3306下面加上max_allowed_packet=?M 修改完后重启mysql服务即可成功上传。
 

显示图片

public ModelAndView
showImage(HttpServletRequest request, HttpServletResponse response)
 throws Exception
{
       String dsbh = (String)
request.getParameter(
"dsbh");
       Integer idsbh = new Integer(dsbh);
       TDs tds = tdsdao.get(idsbh);
       //显示照片。
       streamImage(tds.getZp(),
response.getOutputStream());
       returnnull;
    }
publicvoid streamImage(finalbyte[]
blob,
 final OutputStream contentStream)throws Exception
{
 
       ByteArrayInputStream
is =
 new java.io.ByteArrayInputStream(blob);
       if (is
!=
 null) {
           FileCopyUtils.copy(is,
contentStream);
                  }
           }

 

在JSP叶面要显示照片的地方,我们像如下定义
xml 代码
  1. <image src="/file.do?method=show&id=5"/>  

抱歉!评论已关闭.