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的文件上传功能.