問題:
struts2 使用jakarta 上傳文件時,如果上傳文件的大小超出commons fileupload(jakarta上傳文件還是依賴commons-fileupload)設置的大小就會在進入action以前拋出異常.
如果想返回用戶的輸入界面(input),那么頁面原來的參數會丟失。
首先看一下struts2 執行一個action的過程
1. 將用戶請求發給org.apache.struts2.dispatcher.Dispatcher,
wrapRequest(HttpServletRequest request, ServletContext servletContext) 方法會判斷是否"multipart/form-data",如果是建立一個multiPartRequest 的實例,并且建立MultiPartRequestWrapper
寫道
...if (content_type != null && content_type.indexOf("multipart/form-data") != -1) {
MultiPartRequest multi = getContainer().getInstance(MultiPartRequest.class);
request = new MultiPartRequestWrapper(multi, request, getSaveDir(servletContext));
} else {
request = new StrutsRequestWrapper(request);
}
2. 建立 MultiPartRequestWrapper 時解析(parse) request,
- public void parse(HttpServletRequest servletRequest, String saveDir)
- throws IOException {
- DiskFileItemFactory fac = new DiskFileItemFactory();
-
- fac.setSizeThreshold(0);
- if (saveDir != null) {
- fac.setRepository(new File(saveDir));
- }
-
-
- try {
- ServletFileUpload upload = new ServletFileUpload(fac);
- upload.setSizeMax(maxSize);
-
- List items = upload.parseRequest(createRequestContext(servletRequest));
- ......
-
public void parse(HttpServletRequest servletRequest, String saveDir)
throws IOException {
DiskFileItemFactory fac = new DiskFileItemFactory();
// Make sure that the data is written to file
fac.setSizeThreshold(0);
if (saveDir != null) {
fac.setRepository(new File(saveDir));
}
// Parse the request
try {
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(maxSize);
//upload 解析request并取得頁面參數
List items = upload.parseRequest(createRequestContext(servletRequest));
......
3.我們看一下ServletFileUpload(commons-fileupload v1.1.1) 的parseRequest做了什么
public List /* FileItem */ parseRequest(RequestContext ctx)
throws FileUploadException {
if (ctx == null) {
throw new NullPointerException("ctx parameter");
}
ArrayList items = new ArrayList();
String contentType = ctx.getContentType();
if ((null == contentType)
|| (!contentType.toLowerCase().startsWith(MULTIPART))) {
throw new InvalidContentTypeException(
"the request doesn't contain a "
+ MULTIPART_FORM_DATA
+ " or "
+ MULTIPART_MIXED
+ " stream, content type header is "
+ contentType);
}
int requestSize = ctx.getContentLength();
if (requestSize == -1) {
throw new UnknownSizeException(
"the request was rejected because its size is unknown");
}
//關鍵就這里了,大小超出的異常,這里是所有上傳文件合計的大小,如果超出就拋出異常
//這時上層是拿不到保存參數的items的
if (sizeMax >= 0 && requestSize > sizeMax) {
throw new SizeLimitExceededException(
"the request was rejected because its size (" + requestSize
+ ") exceeds the configured maximum (" + sizeMax + ")",
requestSize, sizeMax);
}
String charEncoding = headerEncoding;
if (charEncoding == null) {
charEncoding = ctx.getCharacterEncoding();
}
try {
byte[] boundary = getBoundary(contentType);
if (boundary == null) {
throw new FileUploadException(
"the request was rejected because "
+ "no multipart boundary was found");
}
InputStream input = ctx.getInputStream();
MultipartStream multi = new MultipartStream(input, boundary);
multi.setHeaderEncoding(charEncoding);
boolean nextPart = multi.skipPreamble();
while (nextPart) {
Map headers = parseHeaders(multi.readHeaders());
String fieldName = getFieldName(headers);
if (fieldName != null) {
String subContentType = getHeader(headers, CONTENT_TYPE);
if (subContentType != null && subContentType
.toLowerCase().startsWith(MULTIPART_MIXED)) {
// Multiple files.
byte[] subBoundary = getBoundary(subContentType);
multi.setBoundary(subBoundary);
boolean nextSubPart = multi.skipPreamble();
while (nextSubPart) {
headers = parseHeaders(multi.readHeaders());
if (getFileName(headers) != null) {
FileItem item =
createItem(headers, false);
OutputStream os = item.getOutputStream();
try {
multi.readBodyData(os);
} finally {
os.close();
}
items.add(item);
} else {
// Ignore anything but files inside
// multipart/mixed.
multi.discardBodyData();
}
nextSubPart = multi.readBoundary();
}
multi.setBoundary(boundary);
} else {
FileItem item = createItem(headers,
getFileName(headers) == null);
OutputStream os = item.getOutputStream();
try {
multi.readBodyData(os);
} finally {
os.close();
}
items.add(item);
}
} else {
// Skip this part.
multi.discardBodyData();
}
nextPart = multi.readBoundary();
}
} catch (IOException e) {
throw new FileUploadException(
"Processing of " + MULTIPART_FORM_DATA
+ " request failed. " + e.getMessage());
}
return items;
}
4.這之后才開始逐個進入interceptor,見DefaultActionInvocation.invoke()
- ....
-
- if (interceptors.hasNext()) {
- final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
- UtilTimerStack.profile("interceptor: "+interceptor.getName(),
- new UtilTimerStack.ProfilingBlock<String>() {
- public String doProfiling() throws Exception {
- resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
- return null;
- }
- });
- } else {
-
- resultCode = invokeActionOnly();
- }
- ...
....
//遞歸interceptor
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
UtilTimerStack.profile("interceptor: "+interceptor.getName(),
new UtilTimerStack.ProfilingBlock<String>() {
public String doProfiling() throws Exception {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
return null;
}
});
} else {
//如果有errors,resultCode會得到‘input’
resultCode = invokeActionOnly();
}
...
5.我們的目標就是返回input并且保留頁面原來的參數,那么就要不要讓ServletFileUpload拋出異常,并且要讓strusts使用我們自己的jakart.
6.寫自己的ServletFileUpload
/*
* Copyright 2001-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.infowarelab.newcentury.web.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.MultipartStream;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.servlet.ServletRequestContext;
import org.apache.log4j.Logger;
/**
* come from commons-fileupload
* @author alfred
*/
public class ServletFileUpload extends FileUpload {
// ---------------------------------------------------------- Class methods
/**
* Logger for this class
*/
private static final Logger logger = Logger.getLogger(ServletFileUpload.class);
private List<String> errors = new ArrayList<String>();
/**
* Constructs an uninitialised instance of this class. A factory must be
* configured, using <code>setFileItemFactory()</code>, before attempting
* to parse requests.
*
* @see FileUpload#FileUpload(FileItemFactory)
*/
public ServletFileUpload() {
super();
}
/**
* Constructs an instance of this class which uses the supplied factory to
* create <code>FileItem</code> instances.
*
* @see FileUpload#FileUpload()
*/
public ServletFileUpload(FileItemFactory fileItemFactory) {
super(fileItemFactory);
}
/**
* overide parseRequest
*/
public List /* FileItem */parseRequest(RequestContext ctx) throws FileUploadException {
if (ctx == null) {
throw new NullPointerException("ctx parameter");
}
ArrayList items = new ArrayList();
String contentType = ctx.getContentType();
if ((null == contentType) || (!contentType.toLowerCase().startsWith(MULTIPART))) {
throw new InvalidContentTypeException("the request doesn't contain a " + MULTIPART_FORM_DATA + " or "
+ MULTIPART_MIXED + " stream, content type header is " + contentType);
}
int requestSize = ctx.getContentLength();
if (requestSize == -1) {
// throw new UnknownSizeException(
// "the request was rejected because its size is unknown");
logger.error("the request was rejected because its size is unknown");
errors.add("the request was rejected because its size is unknown");
}
String charEncoding = getHeaderEncoding();
if (charEncoding == null) {
charEncoding = ctx.getCharacterEncoding();
}
try {
byte[] boundary = getBoundary(contentType);
if (boundary == null) {
// throw new FileUploadException(
// "the request was rejected because "
// + "no multipart boundary was found");
logger.error("the request was rejected because no multipart boundary was found");
errors.add("the request was rejected because no multipart boundary was found");
}
InputStream input = ctx.getInputStream();
MultipartStream multi = new MultipartStream(input, boundary);
multi.setHeaderEncoding(charEncoding);
boolean nextPart = multi.skipPreamble();
while (nextPart) {
Map headers = parseHeaders(multi.readHeaders());
String fieldName = getFieldName(headers);
if (fieldName != null) {
String subContentType = getHeader(headers, CONTENT_TYPE);
if (subContentType != null && subContentType.toLowerCase().startsWith(MULTIPART_MIXED)) {
// Multiple files.
byte[] subBoundary = getBoundary(subContentType);
multi.setBoundary(subBoundary);
boolean nextSubPart = multi.skipPreamble();
while (nextSubPart) {
headers = parseHeaders(multi.readHeaders());
if (getFileName(headers) != null) {
FileItem item = createItem(headers, false);
OutputStream os = item.getOutputStream();
try {
multi.readBodyData(os);
} finally {
os.close();
}
items.add(item);
} else {
// Ignore anything but files inside
// multipart/mixed.
multi.discardBodyData();
}
nextSubPart = multi.readBoundary();
}
multi.setBoundary(boundary);
} else {
FileItem item = createItem(headers, getFileName(headers) == null);
OutputStream os = item.getOutputStream();
try {
multi.readBodyData(os);
} finally {
os.close();
}
items.add(item);
}
} else {
// Skip this part.
multi.discardBodyData();
}
nextPart = multi.readBoundary();
}
// remove SizeLimitExceededException
if (getSizeMax() >= 0 && requestSize > getSizeMax()) {
// throw new SizeLimitExceededException(
// "the request was rejected because its size (" + requestSize
// + ") exceeds the configured maximum (" + getSizeMax() + ")",
// requestSize, getSizeMax());
logger.error("the request was rejected because its size (" + requestSize
+ ") exceeds the configured maximum (" + getSizeMax() + ")");
}
} catch (IOException e) {
logger.error("Processing of " + MULTIPART_FORM_DATA + " request failed. " + e.getMessage());
errors.add("Processing of " + MULTIPART_FORM_DATA + " request failed. " + e.getMessage());
// throw new FileUploadException(
// "Processing of " + MULTIPART_FORM_DATA
// + " request failed. " + e.getMessage());
}
return items;
}
/**
* @return the errors
*/
public List<String> getErrors() {
return errors;
}
/**
* @param errors the errors to set
*/
public void setErrors(List<String> errors) {
this.errors = errors;
}
}
7.copy org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest,只是import上面自己的ServletFileUpload.這樣就可以保存頁面的所有參數了。
8.更改struts配置文件加入你自己的JakartaMultiReques
- <bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest"
- ame="jakarta_yourself"
- class="com.xxxxx.util.JakartaMultiPartRequest"
- cope="default" optional="true" />
<bean type="org.apache.struts2.dispatcher.multipart.MultiPartRequest"
name="jakarta_yourself"
class="com.xxxxx.util.JakartaMultiPartRequest"
scope="default" optional="true" />
9.更改struts.properties
struts.multipart.parser=jakarta_yourself
10.就OK啦
posted on 2009-04-14 12:50
lanxin1020 閱讀(2228)
評論(0) 編輯 收藏 所屬分類:
struts2