提交 57153535 编写于 作者: J Juergen Hoeller

revised Servlet 3.0 based StandardServletMultipartResolver for correct...

revised Servlet 3.0 based StandardServletMultipartResolver for correct param/file distinction; added multipart content type and headers access to MultipartRequest (dropping the previous header access solution on MultipartFile); MultipartFilter uses a Servlet 3.0 based StandardServletMultipartResolver by default
上级 21f3f59c
......@@ -41,8 +41,6 @@ import org.springframework.web.multipart.MultipartFile;
*/
public class MockMultipartFile implements MultipartFile {
private static final String CONTENT_TYPE = "Content-Type";
private final String name;
private String originalFilename;
......@@ -133,26 +131,4 @@ public class MockMultipartFile implements MultipartFile {
FileCopyUtils.copy(this.content, dest);
}
public String getHeader(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.contentType;
}
else {
return null;
}
}
public String[] getHeaders(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return new String[] {this.contentType};
}
else {
return null;
}
}
public Iterator<String> getHeaderNames() {
return Collections.singleton(CONTENT_TYPE).iterator();
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -17,10 +17,13 @@
package org.springframework.mock.web;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
......@@ -89,4 +92,40 @@ public class MockMultipartHttpServletRequest extends MockHttpServletRequest impl
return new LinkedMultiValueMap<String, MultipartFile>(this.multipartFiles);
}
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
return file.getContentType();
}
else {
return null;
}
}
public HttpMethod getRequestMethod() {
return HttpMethod.valueOf(getMethod());
}
public HttpHeaders getRequestHeaders() {
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.put(headerName, Collections.list(getHeaders(headerName)));
}
return headers;
}
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
String contentType = getMultipartContentType(paramOrFileName);
if (contentType != null) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", contentType);
return headers;
}
else {
return null;
}
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -72,7 +72,7 @@ public class MockMultipartActionRequest extends MockActionRequest implements Mul
else {
return Collections.emptyList();
}
}
}
public Map<String, MultipartFile> getFileMap() {
return this.multipartFiles.toSingleValueMap();
......@@ -82,4 +82,14 @@ public class MockMultipartActionRequest extends MockActionRequest implements Mul
return new LinkedMultiValueMap<String, MultipartFile>(this.multipartFiles);
}
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
return file.getContentType();
}
else {
return null;
}
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -126,13 +126,14 @@ public class CommonsPortletMultipartResolver extends CommonsFileUploadSupport
MultipartParsingResult parsingResult = parseRequest(request);
setMultipartFiles(parsingResult.getMultipartFiles());
setMultipartParameters(parsingResult.getMultipartParameters());
setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
}
};
}
else {
MultipartParsingResult parsingResult = parseRequest(request);
return new DefaultMultipartActionRequest(
request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters());
return new DefaultMultipartActionRequest(request, parsingResult.getMultipartFiles(),
parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
}
}
......
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -21,15 +21,15 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.List;
import javax.portlet.ActionRequest;
import javax.portlet.filter.ActionRequestWrapper;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.util.MultiValueMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
/**
* Default implementation of the {@link MultipartActionRequest} interface.
......@@ -46,6 +46,8 @@ public class DefaultMultipartActionRequest extends ActionRequestWrapper implemen
private Map<String, String[]> multipartParameters;
private Map<String, String> multipartParameterContentTypes;
/**
* Wrap the given Portlet ActionRequest in a MultipartActionRequest.
......@@ -54,12 +56,13 @@ public class DefaultMultipartActionRequest extends ActionRequestWrapper implemen
* @param mpParams a map of the parameters to expose,
* with Strings as keys and String arrays as values
*/
public DefaultMultipartActionRequest(
ActionRequest request, MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {
public DefaultMultipartActionRequest(ActionRequest request, MultiValueMap<String, MultipartFile> mpFiles,
Map<String, String[]> mpParams, Map<String, String> mpParamContentTypes) {
super(request);
setMultipartFiles(mpFiles);
setMultipartParameters(mpParams);
setMultipartParameterContentTypes(mpParamContentTypes);
}
/**
......@@ -136,6 +139,16 @@ public class DefaultMultipartActionRequest extends ActionRequestWrapper implemen
return paramMap;
}
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
return file.getContentType();
}
else {
return getMultipartParameterContentTypes().get(paramOrFileName);
}
}
/**
* Set a Map with parameter names as keys and list of MultipartFile objects as values.
......@@ -178,6 +191,27 @@ public class DefaultMultipartActionRequest extends ActionRequestWrapper implemen
return this.multipartParameters;
}
/**
* Set a Map with parameter names as keys and content type Strings as values.
* To be invoked by subclasses on initialization.
*/
protected final void setMultipartParameterContentTypes(Map<String, String> multipartParameterContentTypes) {
this.multipartParameterContentTypes = multipartParameterContentTypes;
}
/**
* Obtain the multipart parameter content type Map for retrieval,
* lazily initializing it if necessary.
* @see #initializeMultipart()
*/
protected Map<String, String> getMultipartParameterContentTypes() {
if (this.multipartParameterContentTypes == null) {
initializeMultipart();
}
return this.multipartParameterContentTypes;
}
/**
* Lazily initialize the multipart request, if possible.
* Only called if not already eagerly initialized.
......
......@@ -41,8 +41,6 @@ import org.springframework.web.multipart.MultipartFile;
*/
public class MockMultipartFile implements MultipartFile {
private static final String CONTENT_TYPE = "Content-Type";
private final String name;
private String originalFilename;
......@@ -133,26 +131,4 @@ public class MockMultipartFile implements MultipartFile {
FileCopyUtils.copy(this.content, dest);
}
public String getHeader(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.contentType;
}
else {
return null;
}
}
public String[] getHeaders(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return new String[] {this.contentType};
}
else {
return null;
}
}
public Iterator<String> getHeaderNames() {
return Collections.singleton(CONTENT_TYPE).iterator();
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -82,4 +82,14 @@ public class MockMultipartActionRequest extends MockActionRequest implements Mul
return new LinkedMultiValueMap<String, MultipartFile>(this.multipartFiles);
}
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
return file.getContentType();
}
else {
return null;
}
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,6 +18,7 @@ package org.springframework.web.portlet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
......@@ -481,7 +482,7 @@ public class ComplexPortletApplicationContext extends StaticPortletApplicationCo
files.set("someFile", new MockMultipartFile("someFile", "someContent".getBytes()));
Map<String, String[]> params = new HashMap<String, String[]>();
params.put("someParam", new String[] {"someParam"});
return new DefaultMultipartActionRequest(request, files, params);
return new DefaultMultipartActionRequest(request, files, params, Collections.<String, String>emptyMap());
}
public void cleanupMultipart(MultipartActionRequest request) {
......
......@@ -39,7 +39,7 @@ import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.RequestPartServletServerHttpRequest;
import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import org.springframework.web.util.WebUtils;
......@@ -123,9 +123,8 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM
arg = servletRequest.getPart(partName);
}
else {
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(multipartRequest, partName);
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, partName);
arg = readWithMessageConverters(inputMessage, parameter, parameter.getParameterType());
if (isValidationApplicable(arg, parameter)) {
WebDataBinder binder = binderFactory.createBinder(request, arg, partName);
binder.validate();
......
......@@ -41,8 +41,6 @@ import org.springframework.web.multipart.MultipartFile;
*/
public class MockMultipartFile implements MultipartFile {
private static final String CONTENT_TYPE = "Content-Type";
private final String name;
private String originalFilename;
......@@ -133,26 +131,4 @@ public class MockMultipartFile implements MultipartFile {
FileCopyUtils.copy(this.content, dest);
}
public String getHeader(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.contentType;
}
else {
return null;
}
}
public String[] getHeaders(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return new String[] {this.contentType};
}
else {
return null;
}
}
public Iterator<String> getHeaderNames() {
return Collections.singleton(CONTENT_TYPE).iterator();
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -17,10 +17,13 @@
package org.springframework.mock.web;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
......@@ -89,4 +92,40 @@ public class MockMultipartHttpServletRequest extends MockHttpServletRequest impl
return new LinkedMultiValueMap<String, MultipartFile>(this.multipartFiles);
}
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
return file.getContentType();
}
else {
return null;
}
}
public HttpMethod getRequestMethod() {
return HttpMethod.valueOf(getMethod());
}
public HttpHeaders getRequestHeaders() {
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.put(headerName, Collections.list(getHeaders(headerName)));
}
return headers;
}
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
String contentType = getMultipartContentType(paramOrFileName);
if (contentType != null) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", contentType);
return headers;
}
else {
return null;
}
}
}
/*
* Copyright 2002-2007 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -20,7 +20,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
......@@ -49,7 +48,7 @@ import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.AbstractMultipartHttpServletRequest;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.springframework.web.servlet.handler.SimpleServletHandlerAdapter;
import org.springframework.web.servlet.handler.SimpleServletPostProcessor;
......@@ -434,8 +433,7 @@ public class ComplexWebApplicationContext extends StaticWebApplicationContext {
throw new IllegalStateException("Already resolved");
}
request.setAttribute("resolved", Boolean.TRUE);
return new AbstractMultipartHttpServletRequest(request) {
};
return new DefaultMultipartHttpServletRequest(request);
}
public void cleanupMultipart(MultipartHttpServletRequest request) {
......
......@@ -60,7 +60,7 @@ import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.RequestPartServletServerHttpRequest;
import org.springframework.web.multipart.support.RequestPartServletServerHttpRequest;
/**
* Test fixture with {@link RequestPartMethodArgumentResolver} and mock {@link HttpMessageConverter}.
......
......@@ -44,13 +44,14 @@ import org.springframework.util.Assert;
*/
public class ServletServerHttpRequest implements ServerHttpRequest {
private static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
protected static final String FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
private static final String POST_METHOD = "POST";
protected static final String FORM_CHARSET = "UTF-8";
private static final String PUT_METHOD = "PUT";
private static final String METHOD_POST = "POST";
private static final String METHOD_PUT = "PUT";
private static final String FORM_CHARSET = "UTF-8";
private final HttpServletRequest servletRequest;
......@@ -115,7 +116,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
private boolean isFormSubmittal(HttpServletRequest request) {
return FORM_CONTENT_TYPE.equals(request.getContentType()) &&
(POST_METHOD.equalsIgnoreCase(request.getMethod()) || PUT_METHOD.equalsIgnoreCase(request.getMethod()));
(METHOD_POST.equalsIgnoreCase(request.getMethod()) || METHOD_PUT.equalsIgnoreCase(request.getMethod()));
}
private InputStream getFormBody(HttpServletRequest request) throws IOException {
......
......@@ -19,7 +19,6 @@ package org.springframework.web.multipart;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
/**
* A representation of an uploaded file received in a multipart request.
......@@ -102,28 +101,4 @@ public interface MultipartFile {
*/
void transferTo(File dest) throws IOException, IllegalStateException;
/**
* Return the first value associated with the given header name, if any.
* @param name the name of the header
* @return the first header value, or <code>null</code> if no such header was found
* @since 3.1
*/
String getHeader(String name);
/**
* Return all values associated with the given header name, if any.
* @param name the name of the header
* @return the header values as an array, or <code>null</code> if no such header was found
* @since 3.1
*/
String[] getHeaders(String name);
/**
* Return an {@link java.util.Iterator} of Strings containing the
* names of headers associated with this file.
* @return the names of the headers
* @since 3.1
*/
Iterator<String> getHeaderNames();
}
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -18,6 +18,9 @@ package org.springframework.web.multipart;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
/**
* Provides additional methods for dealing with multipart content within a
* servlet request, allowing to access uploaded files.
......@@ -44,4 +47,21 @@ import javax.servlet.http.HttpServletRequest;
*/
public interface MultipartHttpServletRequest extends HttpServletRequest, MultipartRequest {
/**
* Return this request's method as a convenient HttpMethod instance.
*/
HttpMethod getRequestMethod();
/**
* Return this request's headers as a convenient HttpHeaders instance.
*/
HttpHeaders getRequestHeaders();
/**
* Return the headers associated with the specified part of the multipart request.
* <p>If the underlying implementation supports access to headers, then all headers are returned.
* Otherwise, the returned headers will include a 'Content-Type' header at the very least.
*/
HttpHeaders getMultipartHeaders(String paramOrFileName);
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -75,4 +75,11 @@ public interface MultipartRequest {
*/
MultiValueMap<String, MultipartFile> getMultiFileMap();
/**
* Determine the content type of the specified request part.
* @param paramOrFileName the name of the part
* @return the associated content type, or <code>null</code> if not defined
*/
String getMultipartContentType(String paramOrFileName);
}
......@@ -217,6 +217,7 @@ public abstract class CommonsFileUploadSupport {
protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {
MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<String, MultipartFile>();
Map<String, String[]> multipartParameters = new HashMap<String, String[]>();
Map<String, String> multipartParameterContentTypes = new HashMap<String, String>();
// Extract multipart files and multipart parameters.
for (FileItem fileItem : fileItems) {
......@@ -248,6 +249,7 @@ public abstract class CommonsFileUploadSupport {
String[] newParam = StringUtils.addStringToArray(curParam, value);
multipartParameters.put(fileItem.getFieldName(), newParam);
}
multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());
}
else {
// multipart file field
......@@ -260,7 +262,7 @@ public abstract class CommonsFileUploadSupport {
}
}
}
return new MultipartParsingResult(multipartFiles, multipartParameters);
return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);
}
/**
......@@ -305,29 +307,26 @@ public abstract class CommonsFileUploadSupport {
private final Map<String, String[]> multipartParameters;
/**
* Create a new MultipartParsingResult.
* @param mpFiles Map of field name to MultipartFile instance
* @param mpParams Map of field name to form field String value
*/
public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {
private final Map<String, String> multipartParameterContentTypes;
public MultipartParsingResult(MultiValueMap<String, MultipartFile> mpFiles,
Map<String, String[]> mpParams, Map<String, String> mpParamContentTypes) {
this.multipartFiles = mpFiles;
this.multipartParameters = mpParams;
this.multipartParameterContentTypes = mpParamContentTypes;
}
/**
* Return the multipart files as Map of field name to MultipartFile instance.
*/
public MultiValueMap<String, MultipartFile> getMultipartFiles() {
return this.multipartFiles;
}
/**
* Return the multipart parameters as Map of field name to form field String value.
*/
public Map<String, String[]> getMultipartParameters() {
return this.multipartParameters;
}
public Map<String, String> getMultipartParameterContentTypes() {
return this.multipartParameterContentTypes;
}
}
}
......@@ -42,8 +42,6 @@ import org.springframework.web.multipart.MultipartFile;
*/
public class CommonsMultipartFile implements MultipartFile, Serializable {
private static final String CONTENT_TYPE = "Content-Type";
protected static final Log logger = LogFactory.getLog(CommonsMultipartFile.class);
private final FileItem fileItem;
......@@ -191,26 +189,4 @@ public class CommonsMultipartFile implements MultipartFile, Serializable {
}
}
public String getHeader(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.fileItem.getContentType();
}
else {
return null;
}
}
public String[] getHeaders(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return new String[] {this.fileItem.getContentType()};
}
else {
return null;
}
}
public Iterator<String> getHeaderNames() {
return Collections.singleton(CONTENT_TYPE).iterator();
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -131,13 +131,14 @@ public class CommonsMultipartResolver extends CommonsFileUploadSupport
MultipartParsingResult parsingResult = parseRequest(request);
setMultipartFiles(parsingResult.getMultipartFiles());
setMultipartParameters(parsingResult.getMultipartParameters());
setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
}
};
}
else {
MultipartParsingResult parsingResult = parseRequest(request);
return new DefaultMultipartHttpServletRequest(
request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters());
return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
}
}
......
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -17,17 +17,19 @@
package org.springframework.web.multipart.support;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.util.LinkedMultiValueMap;
/**
* Abstract base implementation of the MultipartHttpServletRequest interface.
......@@ -38,7 +40,7 @@ import org.springframework.util.LinkedMultiValueMap;
* @since 06.10.2003
*/
public abstract class AbstractMultipartHttpServletRequest extends HttpServletRequestWrapper
implements MultipartHttpServletRequest {
implements MultipartHttpServletRequest {
private MultiValueMap<String, MultipartFile> multipartFiles;
......@@ -52,6 +54,25 @@ public abstract class AbstractMultipartHttpServletRequest extends HttpServletReq
}
@Override
public HttpServletRequest getRequest() {
return (HttpServletRequest) super.getRequest();
}
public HttpMethod getRequestMethod() {
return HttpMethod.valueOf(getRequest().getMethod());
}
public HttpHeaders getRequestHeaders() {
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.put(headerName, Collections.list(getHeaders(headerName)));
}
return headers;
}
public Iterator<String> getFileNames() {
return getMultipartFiles().keySet().iterator();
}
......@@ -78,6 +99,7 @@ public abstract class AbstractMultipartHttpServletRequest extends HttpServletReq
return getMultipartFiles();
}
/**
* Set a Map with parameter names as keys and list of MultipartFile objects as values.
* To be invoked by subclasses on initialization.
......
/*
* Copyright 2002-2008 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -24,8 +24,9 @@ import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.http.HttpHeaders;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
/**
* Default implementation of the
......@@ -40,8 +41,12 @@ import org.springframework.util.MultiValueMap;
*/
public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
private static final String CONTENT_TYPE = "Content-Type";
private Map<String, String[]> multipartParameters;
private Map<String, String> multipartParameterContentTypes;
/**
* Wrap the given HttpServletRequest in a MultipartHttpServletRequest.
......@@ -50,12 +55,13 @@ public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpSer
* @param mpParams a map of the parameters to expose,
* with Strings as keys and String arrays as values
*/
public DefaultMultipartHttpServletRequest(
HttpServletRequest request, MultiValueMap<String, MultipartFile> mpFiles, Map<String, String[]> mpParams) {
public DefaultMultipartHttpServletRequest(HttpServletRequest request, MultiValueMap<String, MultipartFile> mpFiles,
Map<String, String[]> mpParams, Map<String, String> mpParamContentTypes) {
super(request);
setMultipartFiles(mpFiles);
setMultipartParameters(mpParams);
setMultipartParameterContentTypes(mpParamContentTypes);
}
/**
......@@ -105,6 +111,28 @@ public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpSer
return paramMap;
}
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
return file.getContentType();
}
else {
return getMultipartParameterContentTypes().get(paramOrFileName);
}
}
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
String contentType = getMultipartContentType(paramOrFileName);
if (contentType != null) {
HttpHeaders headers = new HttpHeaders();
headers.add(CONTENT_TYPE, contentType);
return headers;
}
else {
return null;
}
}
/**
* Set a Map with parameter names as keys and String array objects as values.
......@@ -126,4 +154,24 @@ public class DefaultMultipartHttpServletRequest extends AbstractMultipartHttpSer
return this.multipartParameters;
}
/**
* Set a Map with parameter names as keys and content type Strings as values.
* To be invoked by subclasses on initialization.
*/
protected final void setMultipartParameterContentTypes(Map<String, String> multipartParameterContentTypes) {
this.multipartParameterContentTypes = multipartParameterContentTypes;
}
/**
* Obtain the multipart parameter content type Map for retrieval,
* lazily initializing it if necessary.
* @see #initializeMultipart()
*/
protected Map<String, String> getMultipartParameterContentTypes() {
if (this.multipartParameterContentTypes == null) {
initializeMultipart();
}
return this.multipartParameterContentTypes;
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -38,6 +38,10 @@ import org.springframework.web.multipart.MultipartResolver;
* on each request, to avoid initialization order issues (when using ContextLoaderServlet,
* the root application context will get initialized <i>after</i> this filter).
*
* <p>If no MultipartResolver bean is found, this filter falls back to a default
* MultipartResolver: {@link StandardServletMultipartResolver} for Servlet 3.0,
* based on a multipart-config section in <code>web.xml</code>.
*
* <p>MultipartResolver lookup is customizable: Override this filter's
* <code>lookupMultipartResolver</code> method to use a custom MultipartResolver
* instance, for example if not using a Spring web application context.
......@@ -61,6 +65,8 @@ public class MultipartFilter extends OncePerRequestFilter {
public static final String DEFAULT_MULTIPART_RESOLVER_BEAN_NAME = "filterMultipartResolver";
private final MultipartResolver defaultMultipartResolver = new StandardServletMultipartResolver();
private String multipartResolverBeanName = DEFAULT_MULTIPART_RESOLVER_BEAN_NAME;
......@@ -122,7 +128,7 @@ public class MultipartFilter extends OncePerRequestFilter {
/**
* Look up the MultipartResolver that this filter should use,
* taking the current HTTP request as argument.
* <p>Default implementation delegates to the <code>lookupMultipartResolver</code>
* <p>The default implementation delegates to the <code>lookupMultipartResolver</code>
* without arguments.
* @return the MultipartResolver to use
* @see #lookupMultipartResolver()
......@@ -140,11 +146,17 @@ public class MultipartFilter extends OncePerRequestFilter {
* @return the MultipartResolver instance, or <code>null</code> if none found
*/
protected MultipartResolver lookupMultipartResolver() {
if (logger.isDebugEnabled()) {
logger.debug("Using MultipartResolver '" + getMultipartResolverBeanName() + "' for MultipartFilter");
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
String beanName = getMultipartResolverBeanName();
if (wac != null && wac.containsBean(beanName)) {
if (logger.isDebugEnabled()) {
logger.debug("Using MultipartResolver '" + beanName + "' for MultipartFilter");
}
return wac.getBean(beanName, MultipartResolver.class);
}
else {
return this.defaultMultipartResolver;
}
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return wac.getBean(getMultipartResolverBeanName(), MultipartResolver.class);
}
}
......@@ -14,85 +14,81 @@
* limitations under the License.
*/
package org.springframework.web.multipart;
package org.springframework.web.multipart.support;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
/**
* {@link ServerHttpRequest} implementation that is based on a part of a {@link MultipartHttpServletRequest}.
* The part is accessed as {@link MultipartFile} and adapted to the ServerHttpRequest contract.
*
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 3.1
*/
public class RequestPartServletServerHttpRequest implements ServerHttpRequest {
public class RequestPartServletServerHttpRequest extends ServletServerHttpRequest {
private final MultipartHttpServletRequest multipartRequest;
private final String partName;
private final MultipartHttpServletRequest request;
private final MultipartFile multipartFile;
private final HttpHeaders headers;
private HttpHeaders headers;
/**
* Creates a new {@link RequestPartServletServerHttpRequest} instance.
*
* @param request the multipart request.
* @param name the name of the part to adapt to the {@link ServerHttpRequest} contract.
* Create a new {@link RequestPartServletServerHttpRequest} instance.
* @param request the multipart request
* @param partName the name of the part to adapt to the {@link ServerHttpRequest} contract
*/
public RequestPartServletServerHttpRequest(MultipartHttpServletRequest request, String name) {
this.request = request;
this.multipartFile = request.getFile(name);
Assert.notNull(multipartFile, "Request part named '" + name + "' not found. " +
"Available request part names: " + request.getMultiFileMap().keySet());
}
public RequestPartServletServerHttpRequest(HttpServletRequest request, String partName) {
super(request);
public HttpMethod getMethod() {
return HttpMethod.valueOf(this.request.getMethod());
}
this.multipartRequest = (request instanceof MultipartHttpServletRequest ?
(MultipartHttpServletRequest) request : new StandardMultipartHttpServletRequest(request));
this.partName = partName;
public URI getURI() {
try {
return new URI(this.request.getScheme(), null, this.request.getServerName(),
this.request.getServerPort(), this.request.getRequestURI(),
this.request.getQueryString(), null);
}
catch (URISyntaxException ex) {
throw new IllegalStateException("Could not get HttpServletRequest URI: " + ex.getMessage(), ex);
this.headers = this.multipartRequest.getMultipartHeaders(this.partName);
if (this.headers == null) {
throw new IllegalArgumentException("No request part found for name '" + this.partName + "'");
}
}
/**
* Returns the headers associated with the part of the multi-part request associated with this instance.
* If the underlying implementation supports access to headers, then all headers are returned.
* Otherwise, the returned headers will have a 'Content-Type' header in the very least.
*/
@Override
public HttpHeaders getHeaders() {
if (this.headers == null) {
this.headers = new HttpHeaders();
Iterator<String> iterator = this.multipartFile.getHeaderNames();
while (iterator.hasNext()) {
String name = iterator.next();
String[] values = this.multipartFile.getHeaders(name);
for (String value : values) {
this.headers.add(name, value);
}
}
}
return this.headers;
}
@Override
public InputStream getBody() throws IOException {
return this.multipartFile.getInputStream();
if (this.multipartRequest instanceof StandardMultipartHttpServletRequest) {
try {
return this.multipartRequest.getPart(this.partName).getInputStream();
}
catch (Exception ex) {
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
else {
MultipartFile file = this.multipartRequest.getFile(this.partName);
if (file != null) {
return file.getInputStream();
}
else {
String paramValue = this.multipartRequest.getParameter(this.partName);
return new ByteArrayInputStream(paramValue.getBytes(FORM_CHARSET));
}
}
}
}
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.web.multipart.support;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import org.springframework.http.HttpHeaders;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
/**
* Spring MultipartHttpServletRequest adapter, wrapping a Servlet 3.0 HttpServletRequest
* and its Part objects. Parameters get exposed through the native request's getParameter
* methods - without any custom processing on our side.
*
* @author Juergen Hoeller
* @since 3.1
*/
public class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
private static final String CONTENT_DISPOSITION = "Content-Disposition";
private static final String FILENAME_KEY = "filename=";
/**
* Create a new StandardMultipartHttpServletRequest wrapper for the given request.
* @param request the servlet request to wrap
* @throws MultipartException if parsing failed
*/
public StandardMultipartHttpServletRequest(HttpServletRequest request) throws MultipartException {
super(request);
try {
Collection<Part> parts = request.getParts();
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<String, MultipartFile>(parts.size());
for (Part part : parts) {
String filename = extractFilename(part.getHeader(CONTENT_DISPOSITION));
if (filename != null) {
files.add(part.getName(), new StandardMultipartFile(part, filename));
}
}
setMultipartFiles(files);
}
catch (Exception ex) {
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
private String extractFilename(String contentDisposition) {
if (contentDisposition == null) {
return null;
}
// TODO: can only handle the typical case at the moment
int startIndex = contentDisposition.indexOf(FILENAME_KEY);
if (startIndex == -1) {
return null;
}
String filename = contentDisposition.substring(startIndex + FILENAME_KEY.length());
if (filename.startsWith("\"")) {
int endIndex = filename.indexOf("\"", 1);
if (endIndex != -1) {
return filename.substring(1, endIndex);
}
}
else {
int endIndex = filename.indexOf(";");
if (endIndex != -1) {
return filename.substring(0, endIndex);
}
}
return filename;
}
public String getMultipartContentType(String paramOrFileName) {
try {
Part part = getPart(paramOrFileName);
return (part != null ? part.getContentType() : null);
}
catch (Exception ex) {
throw new MultipartException("Could not access multipart servlet request", ex);
}
}
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
try {
Part part = getPart(paramOrFileName);
if (part != null) {
HttpHeaders headers = new HttpHeaders();
for (String headerName : part.getHeaderNames()) {
headers.put(headerName, new ArrayList<String>(part.getHeaders(headerName)));
}
return headers;
}
else {
return null;
}
}
catch (Exception ex) {
throw new MultipartException("Could not access multipart servlet request", ex);
}
}
/**
* Spring MultipartFile adapter, wrapping a Servlet 3.0 Part object.
*/
private static class StandardMultipartFile implements MultipartFile {
private final Part part;
private final String filename;
public StandardMultipartFile(Part part, String filename) {
this.part = part;
this.filename = filename;
}
public String getName() {
return this.part.getName();
}
public String getOriginalFilename() {
return this.filename;
}
public String getContentType() {
return this.part.getContentType();
}
public boolean isEmpty() {
return (this.part.getSize() == 0);
}
public long getSize() {
return this.part.getSize();
}
public byte[] getBytes() throws IOException {
return FileCopyUtils.copyToByteArray(this.part.getInputStream());
}
public InputStream getInputStream() throws IOException {
return this.part.getInputStream();
}
public void transferTo(File dest) throws IOException, IllegalStateException {
this.part.write(dest.getPath());
}
}
}
......@@ -16,23 +16,12 @@
package org.springframework.web.multipart.support;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Iterator;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
......@@ -56,9 +45,6 @@ import org.springframework.web.multipart.MultipartResolver;
*/
public class StandardServletMultipartResolver implements MultipartResolver {
protected final Log logger = LogFactory.getLog(getClass());
public boolean isMultipart(HttpServletRequest request) {
// Same check as in Commons FileUpload...
if (!"post".equals(request.getMethod().toLowerCase())) {
......@@ -80,89 +66,7 @@ public class StandardServletMultipartResolver implements MultipartResolver {
}
}
catch (Exception ex) {
logger.warn("Failed to perform cleanup of multipart items", ex);
}
}
/**
* Spring MultipartHttpServletRequest adapter, wrapping a Servlet 3.0 HttpServletRequest
* and its Part objects. Parameters get exposed through the native request's getParameter
* methods - without any custom processing on our side.
*/
private static class StandardMultipartHttpServletRequest extends AbstractMultipartHttpServletRequest {
public StandardMultipartHttpServletRequest(HttpServletRequest request) throws MultipartException {
super(request);
try {
Collection<Part> parts = request.getParts();
MultiValueMap<String, MultipartFile> files = new LinkedMultiValueMap<String, MultipartFile>(parts.size());
for (Part part : parts) {
files.add(part.getName(), new StandardMultipartFile(part));
}
setMultipartFiles(files);
}
catch (Exception ex) {
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
}
/**
* Spring MultipartFile adapter, wrapping a Servlet 3.0 Part object.
*/
private static class StandardMultipartFile implements MultipartFile {
private final Part part;
public StandardMultipartFile(Part part) {
this.part = part;
}
public String getName() {
return this.part.getName();
}
public String getOriginalFilename() {
return null; // not supported in Servlet 3.0 - switch to Commons FileUpload if you need this
}
public String getContentType() {
return this.part.getContentType();
}
public boolean isEmpty() {
return (this.part.getSize() == 0);
}
public long getSize() {
return this.part.getSize();
}
public byte[] getBytes() throws IOException {
return FileCopyUtils.copyToByteArray(this.part.getInputStream());
}
public InputStream getInputStream() throws IOException {
return this.part.getInputStream();
}
public void transferTo(File dest) throws IOException, IllegalStateException {
this.part.write(dest.getPath());
}
public String getHeader(String name) {
return this.part.getHeader(name);
}
public String[] getHeaders(String name) {
Collection<String> headers = this.part.getHeaders(name);
return (headers != null ? StringUtils.toStringArray(headers) : null);
}
public Iterator<String> getHeaderNames() {
return this.part.getHeaderNames().iterator();
LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex);
}
}
......
......@@ -41,8 +41,6 @@ import org.springframework.web.multipart.MultipartFile;
*/
public class MockMultipartFile implements MultipartFile {
private static final String CONTENT_TYPE = "Content-Type";
private final String name;
private String originalFilename;
......@@ -133,26 +131,4 @@ public class MockMultipartFile implements MultipartFile {
FileCopyUtils.copy(this.content, dest);
}
public String getHeader(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return this.contentType;
}
else {
return null;
}
}
public String[] getHeaders(String name) {
if (CONTENT_TYPE.equalsIgnoreCase(name)) {
return new String[] {this.contentType};
}
else {
return null;
}
}
public Iterator<String> getHeaderNames() {
return Collections.singleton(CONTENT_TYPE).iterator();
}
}
/*
* Copyright 2002-2010 the original author or authors.
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -17,10 +17,13 @@
package org.springframework.mock.web;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
......@@ -89,4 +92,40 @@ public class MockMultipartHttpServletRequest extends MockHttpServletRequest impl
return new LinkedMultiValueMap<String, MultipartFile>(this.multipartFiles);
}
public String getMultipartContentType(String paramOrFileName) {
MultipartFile file = getFile(paramOrFileName);
if (file != null) {
return file.getContentType();
}
else {
return null;
}
}
public HttpMethod getRequestMethod() {
return HttpMethod.valueOf(getMethod());
}
public HttpHeaders getRequestHeaders() {
HttpHeaders headers = new HttpHeaders();
Enumeration<String> headerNames = getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.put(headerName, Collections.list(getHeaders(headerName)));
}
return headers;
}
public HttpHeaders getMultipartHeaders(String paramOrFileName) {
String contentType = getMultipartContentType(paramOrFileName);
if (contentType != null) {
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", contentType);
return headers;
}
else {
return null;
}
}
}
......@@ -14,16 +14,13 @@
* limitations under the License.
*/
package org.springframework.web.multipart;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
package org.springframework.web.multipart.support;
import java.net.URI;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
......@@ -31,9 +28,9 @@ import org.springframework.mock.web.MockMultipartFile;
import org.springframework.mock.web.MockMultipartHttpServletRequest;
import org.springframework.util.FileCopyUtils;
import static org.junit.Assert.*;
/**
* Test fixture for {@link RequestPartServletServerHttpRequest} unit tests.
*
* @author Rossen Stoyanchev
*/
public class RequestPartServletServerHttpRequestTests {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册