Struts2 文件上传

 

之前我们可能使用过文件上传组件进行开发系统中文件上传的功能,但在Struts2 中为我们提供了更为简单易用的上传功能,当然虽然易用,但其实际不是对底层文件IOHTTP的封装。下面我们介绍一下,如何在Struts2 中使用文件上传的功能。

我们知识Strust2中大部分的功能都是通过拦截器实现的,当然,这里的文件上传也不例外。同样也是也采用拦截器来支持的。

 

下面以例子来看一下如何使用strsut2 实现文件上传:

1.   myeclipse 下创建一个strust2  web 工程,正常配置struts.xml

2.   web目录下面创建一个upload.jsp 页面,做为页面上传的界面,代码如下:

<%@ page language="java"  contentType="text/html;charset=UTF-8" pageEncoding="UTF-8"%>

<%@ taglib uri="/struts-tags" prefix="s" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

 

    <title>register page</title>

   

    <meta http-equiv="pragma" content="no-cache">

    <meta http-equiv="cache-control" content="no-cache">

    <meta http-equiv="expires" content="0">   

    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">

    <meta http-equiv="description" content="This is my page">

    <!--

    <link rel="stylesheet" type="text/css" href="styles.css">

    -->

    <script type="text/javascript">

        function addMore(){

            var td=document.getElementById("more");

            var br=document.createElement("br");

            var input=document.createElement("input");

            var button=document.createElement("input");

            input.type="file";

            input.name="file";

            button.type="button";

            button.value="remove";

           

            button.onclick=function(){

                td.removeChild(br);

                td.removeChild(input);

                td.removeChild(button);

            }

           

            td.appendChild(br);

            td.appendChild(input);

            td.appendChild(button);

        }

    </script>

  </head>

 

  <body>

    <br>

    <s:fielderror />

    <s:form action="upload" theme="simple" enctype="multipart/form-data" >

    <table border="1" width="500">

         <tr>

             <td colspan="2">文件上传</td>

         </tr>

         <tr>

             <td>username</td>

             <td><s:textfield name="username"/></td>

         </tr>

         <tr>

             <td>password</td>

             <td><s:password name="password" id="password"/></td>

         </tr>

         <tr>

             <td>file1</td>

             <td id="more"><s:file name="file" id="file"/><input type="button" onclick="addMore()" value="添加..."/></td>

         </tr>

         <tr>

             <td></td>

             <td><s:submit name="submit"/></td>

         </tr>

    </table>

    </s:form>

  </body>

</html>

界面显示如下:

 Struts2 文件上传_第1张图片

 

该例子是一个用户可以指定上传文件个数进行上伟的例子,单击添加按钮可以新增上传控制,这个通过javascript 脚本实现的。注意添加的组件的名字都是file,这样上传的文件会传到Action中的一个List中,下面会看到。

1.4创建对应的Action;  UploadAction 代码如下:

package com.snt.struts2.action;

 

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.OutputStream;

import java.util.List;

 

import org.apache.struts2.ServletActionContext;

 

import com.opensymphony.xwork2.ActionSupport;

 

public class UploadAction extends ActionSupport{

         private String username;                           //对应页面的属性

         private String password;

         private List<File> file;                              //上传文件列表

         private List<String> fileFileName;           //上传文件名

         private List<String> fileContentType;   //上传文件内容类型   这两个值strust2会自动注入

         public List<File> getFile() {

                   return file;

         }

         public void setFile(List<File> file) {

                   this.file = file;

         }

         public List<String> getFileContentType() {

                   return fileContentType;

         }

         public void setFileContentType(List<String> fileContentType) {

                   this.fileContentType = fileContentType;

         }

         public List<String> getFileFileName() {

                   return fileFileName;

         }

         public void setFileFileName(List<String> fileFileName) {

                   this.fileFileName = fileFileName;

         }

         public String getPassword() {

                   return password;

         }

         public void setPassword(String password) {

                   this.password = password;

         }

         public String getUsername() {

                   return username;

         }

         public void setUsername(String username) {

                   this.username = username;

         }

         @Override

         public String execute() throws Exception {

                   for(int i=0;i<file.size();i++){

                            InputStream is=new FileInputStream(file.get(i));

                            String root=ServletActionContext.getRequest().getRealPath("/upload");

                            File destFile=new File(root,this.getFileFileName().get(i));

                            OutputStream os=new FileOutputStream(destFile);

                           

                            byte[] buffer=new byte[400];

                            int length=0;

                            while((length=is.read(buffer))>0){

                                     os.write(buffer,0,length);

                            }

                            is.close();

                            os.close();

                   }

                  

                   return SUCCESS;

         }

}

代码中橙色背景显示的代码 List<File> file 对应页面中要上传的文件,映射到Action中就是File 对象,这种映射是由Strust2的拦截器来实现的。

我们查看strust2 中的strust-detault.xml 文件中,会在默认的拦截器链中发一个fileUpload 的拦截,

strust2 核心包中 org.apache.struts2.interceptor.FileUploadIntercepotr 这个拦截器,下面是这个拦截器的拦截方法:

public String intercept(ActionInvocation invocation) throws Exception {

        ActionContext ac = invocation.getInvocationContext();    Action上下文

            //获取用户请求判断是否是文件上传请求

        HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);

        if (!(request instanceof MultiPartRequestWrapper)) {

            if (log.isDebugEnabled()) {

                ActionProxy proxy = invocation.getProxy();

                log.debug(getTextMessage("struts.messages.bypass.request", new Object[]{proxy.getNamespace(), proxy.getActionName()}, ActionContext.getContext().getLocale()));

            }

 

            return invocation.invoke();

        }

           

        final Object action = invocation.getAction();

        ValidationAware validation = null;

            //验证

        if (action instanceof ValidationAware) {

            validation = (ValidationAware) action;

        }

            //获取文件上传请求的一个包装类对象

        MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;

 

        if (multiWrapper.hasErrors()) {

            for (Iterator errorIter = multiWrapper.getErrors().iterator(); errorIter.hasNext();) {

                String error = (String) errorIter.next();

 

                if (validation != null) {

                    validation.addActionError(error);

                }

 

                log.error(error);

            }

        }

            //大家看这句话,是从Action上下文件环境中取出所以的参数

        Map parameters = ac.getParameters();

 

        // Bind allowed Files

             //下面是关键的代码,先取得上传文件的参数

             //然后再设置到Action中的File 对象

        Enumeration fileParameterNames = multiWrapper.getFileParameterNames();

        while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {

            // get the value of this input tag

            String inputName = (String) fileParameterNames.nextElement();

 

            // get the content type

            String[] contentType = multiWrapper.getContentTypes(inputName);

 

            if (isNonEmpty(contentType)) {

                // get the name of the file from the input tag

                String[] fileName = multiWrapper.getFileNames(inputName);

 

                if (isNonEmpty(fileName)) {

                    // Get a File object for the uploaded File

                    File[] files = multiWrapper.getFiles(inputName);

                    if (files != null) {

                        for (int index = 0; index < files.length; index++) {

 

                            if (acceptFile(files[index], contentType[index], inputName, validation, ac.getLocale())) {

                                parameters.put(inputName, files);

                                                            //下面这两行代码下好解释我们在Action中声明的两个属性,

                                                            //fileFileName   fileContentType ,拦截器会自动给它们注入值勤

                                parameters.put(inputName + "ContentType", contentType);

                                parameters.put(inputName + "FileName", fileName);

                            }

                        }

                    }

                } else {

                    log.error(getTextMessage("struts.messages.invalid.file", new Object[]{inputName}, ActionContext.getContext().getLocale()));

                }

            } else {

                log.error(getTextMessage("struts.messages.invalid.content.type", new Object[]{inputName}, ActionContext.getContext().getLocale()));

            }

        }

 

        // invoke action

        String result = invocation.invoke();

 

        // cleanup

        fileParameterNames = multiWrapper.getFileParameterNames();

        while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {

            String inputValue = (String) fileParameterNames.nextElement();

            File[] file = multiWrapper.getFiles(inputValue);

            for (int index = 0; index < file.length; index++) {

                File currentFile = file[index];

                log.info(getTextMessage("struts.messages.removing.file", new Object[]{inputValue, currentFile}, ActionContext.getContext().getLocale()));

 

                if ((currentFile != null) && currentFile.isFile()) {

                    currentFile.delete();

                }

            }

        }

 

        return result;

    }

 

通过这个拦截器,Strsut2 2自动将上传文件对应到Action中的File对象,Action中再通过IO流将其写入到磁盘文件。这就是上传的原理。

execute方法 中的代码是简单的文件流操作。

15上面UploadAction写在,在strust.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.custom.i18n.resources" value="message" />

        <constant name="struts.multipart.saveDir" value="D:/"></constant>

        <package name="struts2" extends="struts-default">

            <!-- 配置拦截器 -->

            <interceptors>

                <!-- 自定义拦截器栈 -->

                <interceptor-stack name="myStack">

                    <interceptor-ref name="defaultStack" />

                </interceptor-stack>

            </interceptors>

            <action name="upload" class="com.snt.struts2.action.UploadAction">

                <!-- 注意拦截顺序,需要覆盖的先配置 -->

                <interceptor-ref name="fileUpload">

                <result name="success">/uploadResult.jsp</result>

                <result name="input">/upload.jsp</result>

            </action>  

        </package>

   </struts>

  

好配置好了!

16 部署应用程序到tomcat 下面,运行测试:

可以正常上传文件。

17 上面的我们实现的文件上传,但是在实际的开发应用中,上传文件的文件类型和大小都是有限制的。

Strust2 中通过配置方式就可以实现。

我们先看一下拦截FileUploadInterceptor 拦截器中的属性如下:

public class FileUploadInterceptor extends AbstractInterceptor {

    private static final long serialVersionUID = -4764627478894962478L;

 

    protected static final Log log = LogFactory.getLog(FileUploadInterceptor.class);

    private static final String DEFAULT_DELIMITER = ",";

    private static final String DEFAULT_MESSAGE = "no.message.found";

 

    protected Long maximumSize;

    protected String allowedTypes;

    protected Set allowedTypesSet = Collections.EMPTY_SET;

当我们看到上面三个属性时,应该可以想到strust2 是如何限制上传文件类型的大小的。

所以我们只需要在配置Action时,修改一个拦截器的配置就可以了,如下配置:

<action name="upload" class="com.snt.struts2.action.UploadAction">

                <!-- 注意拦截顺序,需要覆盖的先配置 -->

                <interceptor-ref name="fileUpload">

                    <param name="maximumSize">409600</param>

                    <param name="allowedTypes">application/vnd.ms-excel</param>

                </interceptor-ref>

                <interceptor-ref name="myStack" />

                <result name="success">/uploadResult.jsp</result>

                <result name="input">/upload.jsp</result>

            </action>

在配置文件上传拦截器时加两个参数配置即可。

再次部署测试应用程序,发现可以如果文件类型不对或大小过限,则页面又跳回上传页面,说明我们配置的起作用,这是因为上传程序出现异常,程序跳转到页面.在控制我们可以看到“严重”的报错信息.

如果我们在页面上加一句: <s:filederror /> ,运行就可以显示出错的原因,但是这样的报错信息显示太不友好。其实报错的信息就是一条Field 级别的错误信息。

如何屏蔽呢?

之前在struts2 输入校验的时候,讲在message.proeprties 文件中配置可以屏蔽校验的出错信息。

所以我们可以在这里配置信息:

但是现在又不知道,报告的信息的key 值是什么?

我们打开sturst2 核心包中strust-message.proeprties 文件,内容如下:

struts.messages.invalid.token=The form has already been processed or no token was supplied, please try again.

struts.internal.invalid.token=Form token {0} does not match the session token {1}.

 

struts.messages.bypass.request=Bypassing {0}/ {1}

struts.messages.current.file=File {0} {1} {2} {3}

struts.messages.invalid.file=Could not find a Filename for {0}. Verify that a valid file was submitted.

struts.messages.invalid.content.type=Could not find a Content-Type for {0}. Verify that a valid file was submitted.

struts.messages.removing.file=Removing file {0} {1}

struts.messages.error.uploading=Error uploading: {0}

struts.messages.error.file.too.large=File too large: {0} "{1}" {2}

struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" {2}

 

我们可以找到其中几个就是我们想要信息。

struts.messages.error.file.too.large 代表上传文件过大会显示的信息

struts.messages.error.content.type.not.allowed 代表上传文件类型不允许,会显示的信息,我们要覆盖这样的信息,需要在classes 目录下[src目录下]

message.proeprteis 文件中配置

truts.messages.error.content.type.not.allowed=/u4e0a/u4f20/u6587/u4ef6/u7c7b/u578b/u4e0d/u5339/u914d,/u8bf7/u91cd/u8bd5

struts.messages.error.file.too.large=/u4e0a/u4f20/u6587/u4ef6/u8fc7/u5927,/u8bf7/u91cd/u8bd5

上面显示的汉字要通过native2ascii 工具进行编码转化。

再次运行测试就可以正常显示我们配置的信息。

到此,我们已经实现了一个完整功能的文件上传例子。

 

当然上面是个多文件上传的例子,如果是单个文件,只要在Action中将List改为单个对象,execute 代码稍作修改即可。

另外需要注意的是strsuts.xml 中配置的两具常量,这个常量:

国际化信息配置文件指定

<constant name="struts.custom.i18n.resources" value="message" />

上传文件的临时保存目录

<constant name="struts.multipart.saveDir" value="D:/"></constant>      

 

 

 

你可能感兴趣的:(String,struts,File,validation,action,文件上传组件)