要想将文件从客户端上传到服务器,首先要将form的enctype属性设置为” multipart/form-data”,method属性设置为post。然后在服务端获取上传的二进制流,解析出二进制流中所包含的全部表单域。表单域中包含的信息可能是文本信息,也可能是二进制的字节码,文本和字节码混合在一起。要想将一个文件中包含的二进制信息从一个二进制流中“抠”出来,我们需要编写大量的代码来处理字节,比较麻烦。我们可以使用一些编写好的文件上传框架来替我们完成从二进制流中提取表单域的工作。对于java而言,比较常用的框架有两个:common-FileUpload和COS,不管使用哪个上传框架,它都负责解析出HttpServletRequest请求中的所有域,不管是文件域还是普通的文本域。当框架替我们解析出文件域后,我们就可以使用流将文件内容写入到服务器中了。
这两个框架都能够完成文件上传,他们使用的方式有一些差异,但是它们都能够在struts2中使用。Struts2并没有提供自己的文件上传解析器,它需要借助于cos框架或者common-fileupload框架.这两个框架在使用的时候有差异,struts2对他们进行了封装,消除了这种差异。也就是说,在struts2中不管使用哪种文件上传框架,对于我们的编码没有任何影响。那么在struts2中,struts2如何知道使用的是哪种文件上传框架呢?在struts2中的核心包(struts2-core-2.3.4.jar)中的default.properties文件中我们可以看到如下的内容:
可见,在struts2中默认使用的是jakaarta的common-fileUpload进行文件上传的。如果要用它进行文件上传,就必须下载两个jar包
(commons-fileupload-1.2.2.jar,commons-io-2.0.1.jar)并加入到classpath中。上传文件大小最大不能超过2M(2097152byte=2M),当然这个值我们可以在struts.xml文件中进行修改。要想换成别的文件上传框架,只需将对应的jar包Copy到classpath中,然后修改常量struts.multipart.parser 即可
如果使用COS框架可去官方下载,它是oreilly组织下的一个小项目,该项目同样可以实现文件上传。
解压后Lib中有一个cos.jar文件就是我们需要使用的jar文件。
下面使用commons-FileUpload来进行文件上传:
这里我使用的是最新的Struts2.3.4,需要加入的包如图
JSP页面,index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>文件上传</title>
</head>
<body>
<s:form action="fileupload" enctype="multipart/form-data" method="post">
<s:textfield name="user" label="上传者" />
<s:file name="upfile" label="选择文件" />
<s:submit value="提交" />
</s:form>
</body>
</html>
编写Action:
package com.jason.web.action;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.servlet.ServletContext;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class FileUploadAction extends ActionSupport{
private static final long serialVersionUID = 2904016238332840263L;
private String user; //上传者
private File upfile; //上传的文件
private String upfileContentType; //文件类型
private String upfileFileName; //上传文件的原始名
private String savePath; //文件在服务器上的保存路径
//省略Getter、Setter方法
}
在strtus.xml中为这个action做配置
<struts>
<!-- 处理中文问题 ,默认配置就是UTF-8-->
<constant name="struts.i18n.encoding" value="UTF-8" />
<package name="com.jason" extends="struts-default">
<action name="fileupload" class="com.jason.web.action.FileUploadAction">
<!-- 注入属性savePath,表示文件上传位置 -->
<param name="savePath">/uploadFile</param>
<result name="disp">/show.jsp</result>
</action>
</package>
</struts>
web.xml需要加入struts2的filter
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Action中的execute方法
@Override
public String execute() throws Exception {
//获取web应用程序在服务器上的路径需要访问ServletAPI
ServletContext sctx=ServletActionContext.getServletContext();
//取得web应用在服务器上的物理路径:
//如"D:/apache-tomcat-6.0.16/webapps/struts2Demo/fileUpload"
String absoluteSavePath=sctx.getRealPath(savePath);
File saveDirectory=new File(absoluteSavePath);
//如果不存在,则创建
if(!saveDirectory.exists()){
saveDirectory.mkdir();
}
//保存的文件名
String saveName=this.createSaveFileName();
//存储到session中
ActionContext.getContext().getSession().put("file",savePath+"/"+saveName );
//保存文件
saveFile(new File(absoluteSavePath+"/"+saveName));
return "disp";
}
createSaveFileName方法
/**
* 获取改名后的文件名
* @return
*/
private String createSaveFileName(){
//获取扩展名
String extension=upfileFileName.substring(upfileFileName.indexOf('.'));
//以系统时间的毫秒数作为文件名
return System.currentTimeMillis()+extension;
}
saveFile方法
/**
* 保存文件
* @param outputFile
*/
private void saveFile(File outputFile){
try {
FileOutputStream fos=new FileOutputStream(outputFile);
BufferedOutputStream bos=new BufferedOutputStream(fos);
FileInputStream fis=new FileInputStream(this.upfile);
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] buffer=new byte[1024];
int length=-1;
while((length=fis.read(buffer))!=-1){
bos.write(buffer,0,length);
}
bos.close();
fos.close();
bis.close();
fis.close();
}catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
show.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>显示页面</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
</head>
<body>
上传成功!相关信息如下:<br />
上传者:<s:property value="user"/> <br />
原始文件名:<s:property value="upfileFileName"/> <br />
文件类型:<s:property value="upfileContentType"/> <br />
保存后的文件:<s:property value="#session.file"/><br />
<img src="${pageContext.request.contextPath}${sessionScope.file}">
</body>
</html>
运行
文件过滤,我们可以限制文件的大小和格式
配置struts.xml
<struts>
<!-- 开发模式-->
<constant name="struts.devMode" value="true"/>
<!-- 处理中文问题 ,默认配置就是UTF-8-->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!-- 处理.do结尾的请求 -->
<constant name="struts.action.extension" value="do"/>
<package name="com.jason" extends="struts-default">
<action name="fileupload" class="com.jason.web.action.FileUploadAction">
<!-- 注入属性savePath,表示文件上传位置 -->
<param name="savePath">/uploadFile</param>
<result name="disp">/show.jsp</result>
<interceptor-ref name="fileUpload">
<param name="allowedTypes">
image/bmp,image/png,image/gif,image/jpeg,image/pjpeg
</param>
<!--不能超过500K -->
<param name="maximumSize">512000</param>
</interceptor-ref>
<!-- 因为一旦为action配置了拦截器,
就不会使用默认的拦截器了,所以需要配置defaultStack-->
<interceptor-ref name="defaultStack" />
<!-- 出错之后,返回的地址 -->
<result name="input">/index.jsp</result>
</action>
</package>
</struts>
测试:如图
出错信息是默认输出的,如果需要输出自定义的错误消息,只需要在国际化的资源文件中配置即可。
Struts.xml中配置国际化资源文件的名称:
<!-- 配置国际化资源文件-->
<constant name="struts.custom.i18n.resources" value="messageResource"/>
messageResource.properties文件:
多文件上传,可以使用数组或List集合实现多文件上传。表单中的文件域的name属性应该是一致的,加入name属性的值是“upload”,则Action中的属性应该这样来定义:
File[] upload
String[] uploadFileName
String[] uploadContentType
或者
List<File> upload
List<String> uploadFileName
List<String> uploadContentType