struts2文件上传及原理

但在Struts2 中为我们提供了更为简单易用的上传功能,当然虽然易用,但其实际不是对底层文件IO和HTTP的封装。下面我们介绍一下,如何在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.οnclick=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" οnclick="addMore()" value="添加..."/></td>

</tr>

<tr>

<td></td>

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

</tr>

</table>

</s:form>

</body>

</html>

界面显示如下:





该例子是一个用户可以指定上传文件个数进行上伟的例子,单击添加按钮可以新增上传控制,这个通过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方法 中的代码是简单的文件流操作。

1.5上面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>



好配置好了!

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

可以正常上传文件。

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

在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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值