很多关于Struts2的文章,对此都有详细的介绍,但是里面的示例的程序写得很糟糕。各种无意义的拦截器技术、国际化技术、初始化参数,把原本简单的程序弄得很繁琐。同时,部分关键的代码没有写。导致每次上传Tomcat都报Unable to find 'struts.multipart.saveDir' property setting.的警告。然后上传大点的文件,整个Web工程就崩溃,然后后台抛出大段的File Upload BaseSize Limit Exceeded Exception异常,刷爆Tomcat。其实这些警告与异常都是可以避免的。我觉得程序就应该更加简短与丰满,而不是雍容华贵。下面举个例子来说明。
一、基本目标
利用Struts2制作一个文件上传系统,限制只能上传少于2M的图片,上传成功后就把图片的中文名、大小,与原本的图片显示。这图片保存到服务器的Upload文件夹。同时,扔一个再大的文件给这个系统,已不会找不到服务器。
二、基本准备
与《【Struts2】Struts2纯手工安装、配置以及Helloworld,以最新版struts 2.3.20 GA做例子》(点击打开链接)一样准备好Struts2必须的jar,同时写好web.xml。这个不解释了。每次说Struts2都要引用一次这个文章。这次不用引入额外的包。
配置好Struts2之后,目录结构如下,在里面新建两个文件夹,一个是未来存放上传文件的Upload,一个是Struts2要求的上传临时文件夹tmp,这个tmp没有也可以,只是会不停报Unable to find 'struts.multipart.saveDir' property setting.的警告。写程序的时候,里面不用写任何东西。
三、制作过程
1、首先是upload.jsp,没什么好说的,就是一个文件表单。只是希望大家注意要对文件域设置好name,一会儿Struts2要根据这个名称接受文件,这里是file。
<%@ 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>选择文件</title>
</head>
<body>
<form method="post" action="upload" enctype="multipart/form-data">
选择文件:<input type="file" name="file" /><br />
<input type="submit" value="上传" />
</form>
</body>
</html>
2、uploaderr.jsp上传失败页面,这个主要是输出后台穿过来的错误信息。errMsg直接利用Jsp2.4的el表达式取就可以了,在《【Filter】利用过滤器Filter解决post传递的编码问题与利用EL表达式简化参数传递》( 点击打开链接)已经提到过。不用像《【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示》( 点击打开链接)利用到c标签,也就是JSTL表达式,更不用使用struts的s标签,就取个小小的值,不用这么复杂。
<%@ 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>上传失败</title>
</head>
<body>
上传失败!${errMsg}<br />
<a href="upload.jsp">返回</a>
</body>
</html>
3、上传成功页面uploadsuc.jsp,同理可得,取出服务器的上的保存名字构造出图片地址,后台传过来的文件名、文件大小,服务器上的保存文件名基本是一个时间戳,不可能给你存中文名的,但就算你要存中文名读取这张图片也没有问题,但是要用到《【Struts2】中文文件的下载与下载权限的控制》( 点击打开链接)的技术
<%@ 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>上传成功</title>
</head>
<body>
上传成功!<br />
文件:${fileFileName},大小:${fileLength}<br />
<img src="./Upload/${saveFileName }" /><br />
<a href="upload.jsp">返回</a>
</body>
</html>
4、struts.xml,这里虽然涉及到文件,但是按照往常一样指定处理的Action也就是java文件,设置根据返回结果跳转到什么链接就可以了。因为无须把文件抛回前台。主要是要在开头要设置两个属性,防止Tomcat抛出异常。也不要像某些书籍、文章那样设置什么拦截器、初始化参数什么的,半点意义没有。只会增加你的记忆负担而已。
<?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.multipart.saveDir" value="/tmp" />
<!--
只是设置struts抛出文件过大异常的大小,这里设置成4G,因为FAT32最大的单文件就是4G
struts2默认抛出文件过大异常的大小为2097152B也就是恰好2M,
超过这个数会在Tomcat吐出一堆异常,
然后前台显示找不到页面,非常不友好。
文件大小的控制不应该利用抛出异常控制,我们自己在java文件中控制就可以
-->
<constant name="struts.multipart.maxSize" value="4294967296" />
<!-- 在test包里面 -->
<package name="test" extends="struts-default">
<!-- action为upload的执行方法就是test包中的upload.java中的run方法 -->
<action name="upload" class="test.upload" method="run">
<result name="success">/uploadsuc.jsp</result>
<result name="error">/uploaderr.jsp</result>
</action>
</package>
</struts>
5、upload.java这是我们核心处理Java,对比与《【Servlet】利用Servlet3.0标准与JSTL表达式实现文件上传系统,支持图片上传后显示》( 点击打开链接)的利用一个Part对象处理完所有东西的Servlet3.0的文件上传,Struts2的文件上传,必须设置一个名称与前台页面传过来页面相同的File对象。在upload.jsp传来File对象的name="file"。因此就有一个File file的静态类成员。之后,Struts2会自动生成一个fileFileName,也就是文件域name属性+FileName的东西,给你包装好用户传递过来的文件名,注意那个N必须大写。当然还有一个name属性+ContentType的String对象给你封装文件类型,但是根本不用利用到这个对象,完全可以根据FileName中.的后缀名去确定文件类型。之后file.length()可以取出文件大小。
好了,这就可以对文件的上传权限进行控制。Struts2还要我们自己写文件存入服务器的方法。这段东西,没有什么解释。Java存文件就是这样存的。
package test;
//用到了输入流,必须的支持
import java.io.*;
//这个是为了取出Upload这个文件夹的绝对路径
import org.apache.struts2.ServletActionContext;
//这是Struts2必要的支持
import com.opensymphony.xwork2.ActionSupport;
//防止报序列号警告
@SuppressWarnings("serial")
//Struts2必须继承这个类
public class upload extends ActionSupport {
//上传文件Action固有的两个属性fileContentType不是必须的
//但一定要设置好getter与setter,这是Struts2固有的特点。
//设置完getter与setter一定不可以在执行方法中再次初始化,如String xx=
//这样前台会取不值的!
private File file;
private String fileFileName;
//saveFileName是保存到服务器的名字
//利用传来过的文件的后缀名+时间戳构造,
//必要时还可以补上用户名
private String saveFileName;
//这是文件的大小,主要是为了传到前台
private String fileLength;
private String errMsg;
//防止输入输出流报警告
@SuppressWarnings("resource")
public String run() throws Exception {
//对错误信息字符串赋予""值,否则会返回前台一个null
errMsg = "";
//判断是否符合上传的条件
boolean canUpload = true;
//取出Upload的绝对路径
String saveFilePath = ServletActionContext.getServletContext()
.getRealPath("/Upload");
//取出上传文件的后缀名
String fileExtensions = getFileFileName().substring(
getFileFileName().lastIndexOf("."));
//根据后缀名判断是否能上传
if (!(fileExtensions.equals(".gif") || fileExtensions.equals(".png")
|| fileExtensions.equals(".jpeg")
|| fileExtensions.equals(".jpg") || fileExtensions
.equals(".bmp"))) {
errMsg += "上传文件只能是图片,后缀名必须是bmp,gif,jpg,jpeg!";
canUpload = false;
}
//根据文件大小判断是否能上传
if (file.length() > (2 * 1024 * 1024)) {
errMsg += "上传文件太大,请少于2M!";
canUpload = false;
}
//把文件大小转化成字符串,推回给前台
fileLength = (file.length() / 1024) + "KB";
//如果可以上传
if (canUpload) {
//就营造一个服务器上的文件名
saveFileName = System.currentTimeMillis() + fileExtensions;
//输出流是保存到服务器的输出流
FileOutputStream fos = new FileOutputStream(saveFilePath + "/"
+ saveFileName);
//输入流就是传过来的文件
FileInputStream fis = new FileInputStream(getFile());
//缓冲数组
byte[] buffer = new byte[1024];
int len = 0;
//输入流不停读东西到缓冲数组,直到读完
while ((len = fis.read(buffer)) > 0) {
//输出流不停把缓冲数组的东西的东西写到服务器上Upload的绝对目录上面
fos.write(buffer, 0, len);
}
//返回struts.xml一个成功结果
return "success";
} else {
return "error";
}
}
//所有静态成员必须有getter与setter,否则不能与前台交互
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public String getFileFileName() {
return fileFileName;
}
public void setFileFileName(String fileFileName) {
this.fileFileName = fileFileName;
}
public String getSaveFileName() {
return saveFileName;
}
public void setSaveFileName(String saveFileName) {
this.saveFileName = saveFileName;
}
public String getErrMsg() {
return errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
public String getFileLength() {
return fileLength;
}
public void setFileLength(String fileLength) {
this.fileLength = fileLength;
}
}