Struts2框架实现文件上传

Struts2框架实现文件上传

  • 开发环境
    • 系统:Windows10 版本1709
    • JDK:1.8.0_161 32位
    • IDE: Spring Tool Suite 3.7.3 32位
    • Struts2版本:2.3.24
    • 服务器版本:apache-tomcat-7.0.52

单文件的上传

环境搭建

  • 导入必要的jar包

    必须有commons-fileupload-1.3.1.jar包. 在开发Struts2框架必备的13个jar包中,包含了此jar包.

  • 在web.xml中配置核心的过滤器

    <filter>
          <filter-name>struts</filter-name>
          <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
      
    <filter-mapping>
        <filter-name>struts</filter-name>
        <url-pattern>/*</url-pattern>     
    </filter-mapping>  
    

  • 在src目录下创建struts.xml文件,并导入约束

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

文件上传的浏览器的三要素

  • 表单的提交方式必须为post

  • 表单中必须有一个<input type="file">

  • 表单设置enctype="multipart/form-data"

    创建一个upload.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <form action="${pageContext.request.contextPath}/up" method="post" enctype="multipart/form-data">
        <input type="file" name="upload">
        <input type="summit" value="上传">
    </form>
</body>
</html>

文件上传的服务器端

创建一个UploadAction的java类

public class UploadAction {
    
    private File upload;//定义一个File ,变量名要与jsp中的input标签的name一致
    private String uploadContentType;//上传文件的mimeType类型
    private String uploadFileName;//上传文件的名称
    
    public File getUpload() {
        return upload;
    }
    public void setUpload(File upload) {
        this.upload = upload;
    }
    public String getUploadContentType() {
        return uploadContentType;
    }
    public void setUploadContentType(String uploadContentType) {
        this.uploadContentType = uploadContentType;
    }
    public String getUploadFileName() {
        return uploadFileName;
    }
    public void setUploadFileName(String uploadFileName) {
        this.uploadFileName = uploadFileName;
    }
    
    public void uploadFile(){
        try {
            //在WebContent下新建一个upload的文件夹,获取其在服务器的绝对磁盘路径
            String path = ServletActionContext.getServletContext().getRealPath("/upload");
            
            //创建一个服务器端的文件
            File dest = new File(path,uploadFileName);
            
            //完成文件上传的操作
            FileUtils.copyFile(upload, dest);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在struts.xml中进行如下的配置

<struts>
    <!--配置开发者模式,可以显示更加详细的错误信息 -->
    <constant name="struts.devMode" value="true"></constant>
    <constant name="struts.multipart.maxSize" value="20971520"></constant>
    <package name="upload_demo" extends="struts-default" namespace="/" >
        <!-- <interceptors>
            <interceptor-stack name="myStack">
                <interceptor-ref name="fileUpload">
                    设置上传文件允许的后缀名,多个使用逗号隔开 
                    <param name="allowedExtensions">avi,txt</param>
                </interceptor-ref>
                配置了其他的拦截器,就需要手动导入默认的拦截器
                <interceptor-ref name="defaultStack"></interceptor-ref>
            </interceptor-stack>
        </interceptors> -->
        <action class="com.thc.action.UploadAction" method="uploadFile" name="up">
            <result name="input">/error.jsp</result>
            <interceptor-ref name="fileUpload">
                <param name="maximumSize"></param><!-- 设置每一个action允许的上传文件的大小 -->
                <param name="allowedTypes"></param><!--设置上传允许的文件的mimeType类型,多个使用逗号分开-->
                <param name="allowedExtensions">bmp,txt</param><!-- 设置上传文件允许的后缀名,多个使用逗号分开 -->
            </interceptor-ref>
            <interceptor-ref name="defaultStack"></interceptor-ref>
        </action>
    </package>
</struts>

使用Struts2框架实现文件上传.在页面的<input type="file" name="upload"> name属性的名字,必须与文件上传的Java类中定义的File类型变量的名字一样.

文件类型和文件名称的变量名也必须为name之后的拼接

注意上传的文件是个临时文件,服务器重启后,文件就不存在了. 要想永久的保存,需要指定一个本地硬盘的目录.

文件上传出现的问题以及解决方案

文件的大小问题

如果上传一个较大的文件,控制台会报空指针异常.这个空指针的异常信息不够详细.

把当前的文件上传的java类继承ActionSupport类底层会去调用保存错误信息的方法. 并且在struts.xml文件中,开启开发者模式.

<constant name="struts.devMode" value="true"></constant>

再次上传一个较大的文件,会发现这次在报的错误不再是空指针异常. 控制台报错信息如下:

[http-bio-8080-exec-10] ERROR org.apache.struts2.dispatcher.Dispatcher - Could not find action or result
/strust2_fileupload/up
com.opensymphony.xwork2.config.ConfigurationException: No result defined for action com.thc.action.UploadAction and result input

这是说当前的文件上传的java类没有配置result 的input视图.

这是因为Struts底层的拦截器如果出现问题,那么就会把错误信息存储到action中, workflow的拦截器负责控制跳转到指定的input视图.

在struts.xml中的<action> 标签中配置<result>名为input的视图 ,标签体为error.jsp

<struts>
    <!--配置开发者模式,可以显示更加详细的错误信息 -->
    <constant name="struts.devMode" value="true"></constant>
    <package name="upload_demo" extends="struts-default" namespace="/" >
        <action name="up" class="com.thc.action.UploadAction" method="uploadFile">
        <result name="input">/error.jsp</result>
        </action>
    </package>
</struts>

在WebContent下新建一个error.jsp页面

在jsp页面的顶部导入struts2框架的标签

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

在页面中写上struts的标签<s:actionerror/><s:fielderror/> 用来显示提示错误信息

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>错误信息显示页面</title>
</head>
<body>
    <h3>文件上传错误信息显示页面</h3>
    <s:actionerror/>
    <s:fielderror/>
</body>
</html>

以上的操作做完了之后, 再次上传一个较大的文件.会在浏览器上看到如下的错误提示信息

Request exceeded allowed size limit! Max size allowed is: 2,097,152 but request was: 1,346,882,290!

提示是说,请求超出了最大的允许界限

原因是在struts2框架default.properties配置文件中,定义了默认的文件上传大小为2097152字节(byte)为2M.

在struts.xml的配置文件中,修改这个常量的大小.

<constant name="struts.multipart.maxSize" value="20971520"></constant>

修改为20M. 再次上传一个小于20M的文件后,就不会报错了. 这个文件大小的范围,依据实际的需求而设定.

以上的改变常量方法是对所有的action生效,要想仅仅使一个action生效,设置上传文件的大小,需要在<action>标签中设置

查看FileUploadInterceptor这个拦截器的源码

发现有如下的属性:

    protected Long maximumSize;
    protected Set<String> allowedTypesSet = Collections.emptySet();
    protected Set<String> allowedExtensionsSet = Collections.emptySet();

分别代表的是设置文件的最大大小,设置运行的文件类型,设置文件运行的后缀名.

有如下的set方法,设置参数.

 /**
     * Sets the allowed extensions
     *
     * @param allowedExtensions A comma-delimited list of extensions
     */
    public void setAllowedExtensions(String allowedExtensions) {
        allowedExtensionsSet = TextParseUtil.commaDelimitedStringToSet(allowedExtensions);
    }

    /**
     * Sets the allowed mimetypes
     *
     * @param allowedTypes A comma-delimited list of types
     */
    public void setAllowedTypes(String allowedTypes) {
        allowedTypesSet = TextParseUtil.commaDelimitedStringToSet(allowedTypes);
    }

    /**
     * Sets the maximum size of an uploaded file
     *
     * @param maximumSize The maximum size in bytes
     */
    public void setMaximumSize(Long maximumSize) {
        this.maximumSize = maximumSize;
    }

上传文件允许的后缀名

在如下的struts.xml中,配置如下:

<action name="up" class="com.thc.action.UploadAction" method="uploadFile">
        <result name="input">/error.jsp</result>
        <interceptor-ref name="fileUpload">
            <!--设置上传文件允许的后缀名,多个使用逗号隔开  -->
            <param name="allowedExtensions">avi,txt</param>
        </interceptor-ref>
        <!-- 配置了其他的拦截器,就需要手动导入默认的拦截器 -->
        <interceptor-ref name="defaultStack"></interceptor-ref>
</action>

设置了只允许上传后缀名为avi,txt结尾的文件

实验上传一个.doc后缀名的文件

浏览器上会有如下的报错信息 :提示了doc后缀名是不允许的.

File extension not allowed: upload "测试.doc" "upload_7cd79feb_ad18_40cb_a401_f74b2d2f27a1_00000000.tmp" application/msword

多文件的上传

新建一个multipload.jsp的页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h2>多文件的上传</h2>
    <form action="${pageContext.request.contextPath}/multiup" method="post" enctype="multipart/form-data">
    <input type="file" name="upload"><br/>
    <input type="file" name="upload"><br/>
    <input type="file" name="upload">
    <input type="submit" value="上传">
    </form>
</body>
</html>

设置了三个文件上传框

新建一个MultiUploadAction

在原来的属性中,把FIle设置为List集合,或者是数组.

以下为设置了数组.遍历每一个数组的元素.通过索引获取数组的文件名和文件.

public class MultiUploadAction extends ActionSupport {
    
    private File[] upload;//定义一个File ,变量名要与jsp中的input标签的name一致
    private String[] uploadContentType;//上传文件的mimeType类型
    private String[] uploadFileName;//上传文件的名称

    public File[] getUpload() {
        return upload;
    }
    public void setUpload(File[] upload) {
        this.upload = upload;
    }
    public String[] getUploadContentType() {
        return uploadContentType;
    }
    public void setUploadContentType(String[] uploadContentType) {
        this.uploadContentType = uploadContentType;
    }
    public String[] getUploadFileName() {
        return uploadFileName;
    }
    public void setUploadFileName(String[] uploadFileName) {
        this.uploadFileName = uploadFileName;
    }

    public void uploadFile(){
        //在WebContent下新建一个upload的文件夹,获取其在服务器的绝对磁盘路径
        String path = ServletActionContext.getServletContext().getRealPath("/upload");
        try {
            for (int i = 0; i < upload.length; i++) {
                //创建一个服务器端的文件
                File dest = new File(path,uploadFileName[i]);
                //完成文件上传的操作
                FileUtils.copyFile(upload[i], dest);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以下是List集合的方式

通过list.size()来控制集合的遍历,通过get(index)来获取集合的每一个元素

public class MultiUploadAction extends ActionSupport {
    
    private List<File> upload;//定义一个File ,变量名要与jsp中的input标签的name一致
    private List<String> uploadContentType;//上传文件的mimeType类型
    private List<String> uploadFileName;//上传文件的名称

    public List<File> getUpload() {
        return upload;
    }
    public void setUpload(List<File> upload) {
        this.upload = upload;
    }
    public List<String> getUploadContentType() {
        return uploadContentType;
    }
    public void setUploadContentType(List<String> uploadContentType) {
        this.uploadContentType = uploadContentType;
    }
    public List<String> getUploadFileName() {
        return uploadFileName;
    }
    public void setUploadFileName(List<String> uploadFileName) {
        this.uploadFileName = uploadFileName;
    }
    
    public void uploadFile(){
        //在WebContent下新建一个upload的文件夹,获取其在服务器的绝对磁盘路径
        String path = ServletActionContext.getServletContext().getRealPath("/upload");
        try {
            for (int i = 0; i < upload.size(); i++) {
                //创建一个服务器端的文件
                File dest = new File(path,uploadFileName.get(i));
                //完成文件上传的操作
                FileUtils.copyFile(upload.get(i), dest);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在配置文件中新建一个action的配置

<action name="multiup" class="com.thc.action.MultiUploadAction" method="uploadFile">
        <result name="input">/error.jsp</result>
</action>

多文件上传的测试

在服务器中部署项目,访问multipload.jsp页面

选择三个小于2M的文件进行选择文件上传测试.点击上传后,成功在服务器的upload文件夹下上传了文件

注解开发

注解配置文件上传

  • 注解的方式配置action类 单个的文件上传
    • 在class 上配置namespace 和parentPackage
    • 注意单文件的上传@ParentPackage的值为"upload_demo",因为在struts.xml设置了拦截器,起了包名为"upload_demo"
    • 在方法上配置@Action @InterceptorRef @Result
@Namespace("/")
@ParentPackage("upload_demo")
public class UploadAction extends ActionSupport {
    private File upload;//定义一个File ,变量名要与jsp中的input标签的name一致
    private String uploadContentType;//上传文件的mimeType类型
    private String uploadFileName;//上传文件的名称
    public File getUpload() {
        return upload;
    }
    public void setUpload(File upload) {
        this.upload = upload;
    }
    public String getUploadContentType() {
        return uploadContentType;
    }
    public void setUploadContentType(String uploadContentType) {
        this.uploadContentType = uploadContentType;
    }
    public String getUploadFileName() {
        return uploadFileName;
    }
    public void setUploadFileName(String uploadFileName) {
        this.uploadFileName = uploadFileName;
    }
    
    @Action(value="up",interceptorRefs={@InterceptorRef("myStack")},results={@Result(name="input",location="/error.jsp")})
    public void uploadFile(){
        try {
            //在WebContent下新建一个upload的文件夹,获取其在服务器的绝对磁盘路径
            String path = ServletActionContext.getServletContext().getRealPath("/upload");
            //创建一个服务器端的文件
            File dest = new File(path,uploadFileName);
            //完成文件上传的操作
            FileUtils.copyFile(upload, dest);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 注解的方式的配置多文件的上传

    @Namespace("/")
    @ParentPackage("struts-default")
    public class MultiUploadAction extends ActionSupport {
      
      private List<File> upload;//定义一个File ,变量名要与jsp中的input标签的name一致
      private List<String> uploadContentType;//上传文件的mimeType类型
      private List<String> uploadFileName;//上传文件的名称
    
      public List<File> getUpload() {
          return upload;
      }
      public void setUpload(List<File> upload) {
          this.upload = upload;
      }
      public List<String> getUploadContentType() {
          return uploadContentType;
      }
      public void setUploadContentType(List<String> uploadContentType) {
          this.uploadContentType = uploadContentType;
      }
      public List<String> getUploadFileName() {
          return uploadFileName;
      }
      public void setUploadFileName(List<String> uploadFileName) {
          this.uploadFileName = uploadFileName;
      }
      @Action(value="multiup",results={@Result(name="input",location="/error.jsp")})
      public void uploadFile(){
          //在WebContent下新建一个upload的文件夹,获取其在服务器的绝对磁盘路径
          String path = ServletActionContext.getServletContext().getRealPath("/upload");
          try {
              for (int i = 0; i < upload.size(); i++) {
                  //创建一个服务器端的文件
                  File dest = new File(path,uploadFileName.get(i));
                  //完成文件上传的操作
                  FileUtils.copyFile(upload.get(i), dest);
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    }
    



* struts.xml中的内容如下

```xml-dtd
<struts>
  <!--配置开发者模式,可以显示更加详细的错误信息 -->
  <constant name="struts.devMode" value="true"></constant>
  <constant name="struts.multipart.maxSize" value="20971520"></constant>
  <package name="upload_demo" extends="struts-default" namespace="/" >
      <interceptors>
          <interceptor-stack name="myStack">
              <interceptor-ref name="fileUpload">
                  <!--设置上传文件允许的后缀名,多个使用逗号隔开  -->
                  <param name="allowedExtensions">avi,txt</param>
              </interceptor-ref>
              <!-- 配置了其他的拦截器,就需要手动导入默认的拦截器 -->
              <interceptor-ref name="defaultStack"></interceptor-ref>
          </interceptor-stack>
      </interceptors>
  </package>
</struts>

总结

  • 相比较于使用单纯的javaweb开发FileUpload来实现文件上传,使用Struts2框架的文件上传更加的简洁方便,相关的文件上传的控制也更加的方便.但需要对Struts2框架有所熟悉,才能灵活的运用Strust2的文件上传功能.
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容