From 571535352bd2eeb808ee65c9492c241320fb73a0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 20 Jul 2011 20:46:53 +0000 Subject: [PATCH] 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 --- .../mock/web/MockMultipartFile.java | 24 --- .../web/MockMultipartHttpServletRequest.java | 41 +++- .../portlet/MockMultipartActionRequest.java | 14 +- .../CommonsPortletMultipartResolver.java | 7 +- .../DefaultMultipartActionRequest.java | 46 ++++- .../mock/web/MockMultipartFile.java | 24 --- .../portlet/MockMultipartActionRequest.java | 12 +- .../ComplexPortletApplicationContext.java | 5 +- .../RequestPartMethodArgumentResolver.java | 5 +- .../mock/web/MockMultipartFile.java | 24 --- .../web/MockMultipartHttpServletRequest.java | 41 +++- .../servlet/ComplexWebApplicationContext.java | 8 +- ...equestPartMethodArgumentResolverTests.java | 2 +- .../http/server/ServletServerHttpRequest.java | 11 +- .../web/multipart/MultipartFile.java | 25 --- .../MultipartHttpServletRequest.java | 22 ++- .../web/multipart/MultipartRequest.java | 9 +- .../RequestPartServletServerHttpRequest.java | 98 ---------- .../commons/CommonsFileUploadSupport.java | 25 ++- .../commons/CommonsMultipartFile.java | 24 --- .../commons/CommonsMultipartResolver.java | 7 +- .../AbstractMultipartHttpServletRequest.java | 34 +++- .../DefaultMultipartHttpServletRequest.java | 56 +++++- .../multipart/support/MultipartFilter.java | 24 ++- .../RequestPartServletServerHttpRequest.java | 94 ++++++++++ .../StandardMultipartHttpServletRequest.java | 175 ++++++++++++++++++ .../StandardServletMultipartResolver.java | 98 +--------- .../mock/web/MockMultipartFile.java | 24 --- .../web/MockMultipartHttpServletRequest.java | 41 +++- ...uestPartServletServerHttpRequestTests.java | 11 +- 30 files changed, 619 insertions(+), 412 deletions(-) delete mode 100644 org.springframework.web/src/main/java/org/springframework/web/multipart/RequestPartServletServerHttpRequest.java create mode 100644 org.springframework.web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java create mode 100644 org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java rename org.springframework.web/src/test/java/org/springframework/web/multipart/{ => support}/RequestPartServletServerHttpRequestTests.java (90%) diff --git a/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartFile.java b/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartFile.java index 6b87593a167..24518968c38 100644 --- a/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartFile.java +++ b/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartFile.java @@ -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 getHeaderNames() { - return Collections.singleton(CONTENT_TYPE).iterator(); - } - } diff --git a/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java b/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java index c0a7bc84c98..b01c4fdcf84 100644 --- a/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java +++ b/org.springframework.test/src/main/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * 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(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 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; + } + } + } diff --git a/org.springframework.test/src/main/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java b/org.springframework.test/src/main/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java index 2f7fa36c429..7df8b547b09 100644 --- a/org.springframework.test/src/main/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java +++ b/org.springframework.test/src/main/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java @@ -1,5 +1,5 @@ /* - * 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 getFileMap() { return this.multipartFiles.toSingleValueMap(); @@ -82,4 +82,14 @@ public class MockMultipartActionRequest extends MockActionRequest implements Mul return new LinkedMultiValueMap(this.multipartFiles); } + public String getMultipartContentType(String paramOrFileName) { + MultipartFile file = getFile(paramOrFileName); + if (file != null) { + return file.getContentType(); + } + else { + return null; + } + } + } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java index 8acf9cea6eb..c480ac731c4 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/CommonsPortletMultipartResolver.java @@ -1,5 +1,5 @@ /* - * 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()); } } diff --git a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java index 79b81ec7953..ca644eaddcd 100644 --- a/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java +++ b/org.springframework.web.portlet/src/main/java/org/springframework/web/portlet/multipart/DefaultMultipartActionRequest.java @@ -1,5 +1,5 @@ /* - * 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 multipartParameters; + private Map 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 mpFiles, Map mpParams) { + public DefaultMultipartActionRequest(ActionRequest request, MultiValueMap mpFiles, + Map mpParams, Map 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 multipartParameterContentTypes) { + this.multipartParameterContentTypes = multipartParameterContentTypes; + } + + /** + * Obtain the multipart parameter content type Map for retrieval, + * lazily initializing it if necessary. + * @see #initializeMultipart() + */ + protected Map getMultipartParameterContentTypes() { + if (this.multipartParameterContentTypes == null) { + initializeMultipart(); + } + return this.multipartParameterContentTypes; + } + + /** * Lazily initialize the multipart request, if possible. * Only called if not already eagerly initialized. diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java index 6b87593a167..24518968c38 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java @@ -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 getHeaderNames() { - return Collections.singleton(CONTENT_TYPE).iterator(); - } - } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java index 25520f448d3..7df8b547b09 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/mock/web/portlet/MockMultipartActionRequest.java @@ -1,5 +1,5 @@ /* - * 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(this.multipartFiles); } + public String getMultipartContentType(String paramOrFileName) { + MultipartFile file = getFile(paramOrFileName); + if (file != null) { + return file.getContentType(); + } + else { + return null; + } + } + } diff --git a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java index 8bffa583ad6..fbbdcaefaa3 100644 --- a/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java +++ b/org.springframework.web.portlet/src/test/java/org/springframework/web/portlet/ComplexPortletApplicationContext.java @@ -1,5 +1,5 @@ /* - * 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 params = new HashMap(); params.put("someParam", new String[] {"someParam"}); - return new DefaultMultipartActionRequest(request, files, params); + return new DefaultMultipartActionRequest(request, files, params, Collections.emptyMap()); } public void cleanupMultipart(MultipartActionRequest request) { diff --git a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java index 4bf54cae7d6..211cce1364b 100644 --- a/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java +++ b/org.springframework.web.servlet/src/main/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolver.java @@ -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(); diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java b/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java index c9271fcddfb..e91936de863 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartFile.java @@ -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 getHeaderNames() { - return Collections.singleton(CONTENT_TYPE).iterator(); - } - } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java b/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java index a327e3362da..e8720dfa0ac 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * 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(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 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; + } + } + } diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/ComplexWebApplicationContext.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/ComplexWebApplicationContext.java index c82f402608d..ace849d3cd4 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/ComplexWebApplicationContext.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/ComplexWebApplicationContext.java @@ -1,5 +1,5 @@ /* - * 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) { diff --git a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java index acafb9f5984..0f91e296201 100644 --- a/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java +++ b/org.springframework.web.servlet/src/test/java/org/springframework/web/servlet/mvc/method/annotation/support/RequestPartMethodArgumentResolverTests.java @@ -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}. diff --git a/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index 28339d375e9..048114d4b49 100644 --- a/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -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 { diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartFile.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartFile.java index 8fdb1b23ae3..b9979083e3a 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartFile.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartFile.java @@ -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 null 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 null 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 getHeaderNames(); - } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartHttpServletRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartHttpServletRequest.java index 249e508b3eb..c5428f08d4b 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartHttpServletRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * 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. + *

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); + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java index 857b947e2d8..5b3f59087a3 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/MultipartRequest.java @@ -1,5 +1,5 @@ /* - * 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 getMultiFileMap(); + /** + * Determine the content type of the specified request part. + * @param paramOrFileName the name of the part + * @return the associated content type, or null if not defined + */ + String getMultipartContentType(String paramOrFileName); + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/RequestPartServletServerHttpRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/RequestPartServletServerHttpRequest.java deleted file mode 100644 index 5bad0c5e9d9..00000000000 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/RequestPartServletServerHttpRequest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Iterator; - -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.server.ServerHttpRequest; -import org.springframework.util.Assert; - -/** - * {@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 - * @since 3.1 - */ -public class RequestPartServletServerHttpRequest implements ServerHttpRequest { - - private final MultipartHttpServletRequest request; - - private final MultipartFile multipartFile; - - 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. - */ - 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 HttpMethod getMethod() { - return HttpMethod.valueOf(this.request.getMethod()); - } - - 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); - } - } - - /** - * 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. - */ - public HttpHeaders getHeaders() { - if (this.headers == null) { - this.headers = new HttpHeaders(); - Iterator 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; - } - - public InputStream getBody() throws IOException { - return this.multipartFile.getInputStream(); - } - -} diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsFileUploadSupport.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsFileUploadSupport.java index 5745a275858..d2e5938d291 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsFileUploadSupport.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsFileUploadSupport.java @@ -217,6 +217,7 @@ public abstract class CommonsFileUploadSupport { protected MultipartParsingResult parseFileItems(List fileItems, String encoding) { MultiValueMap multipartFiles = new LinkedMultiValueMap(); Map multipartParameters = new HashMap(); + Map multipartParameterContentTypes = new HashMap(); // 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 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 mpFiles, Map mpParams) { + private final Map multipartParameterContentTypes; + + public MultipartParsingResult(MultiValueMap mpFiles, + Map mpParams, Map mpParamContentTypes) { this.multipartFiles = mpFiles; this.multipartParameters = mpParams; + this.multipartParameterContentTypes = mpParamContentTypes; } - /** - * Return the multipart files as Map of field name to MultipartFile instance. - */ public MultiValueMap getMultipartFiles() { return this.multipartFiles; } - /** - * Return the multipart parameters as Map of field name to form field String value. - */ public Map getMultipartParameters() { return this.multipartParameters; } + + public Map getMultipartParameterContentTypes() { + return this.multipartParameterContentTypes; + } } } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java index f1097a7fefa..9c2d45113b1 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartFile.java @@ -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 getHeaderNames() { - return Collections.singleton(CONTENT_TYPE).iterator(); - } - } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java index 23c25e52696..3ea1edc79df 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/commons/CommonsMultipartResolver.java @@ -1,5 +1,5 @@ /* - * 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()); } } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java index e8fa09c9dec..4dd8c14b016 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/AbstractMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * 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 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 headerNames = getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + headers.put(headerName, Collections.list(getHeaders(headerName))); + } + return headers; + } + public Iterator 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. diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/DefaultMultipartHttpServletRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/DefaultMultipartHttpServletRequest.java index 0a3d4590d9c..19d435b77d3 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/DefaultMultipartHttpServletRequest.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/DefaultMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * 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 multipartParameters; + private Map 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 mpFiles, Map mpParams) { + public DefaultMultipartHttpServletRequest(HttpServletRequest request, MultiValueMap mpFiles, + Map mpParams, Map 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 multipartParameterContentTypes) { + this.multipartParameterContentTypes = multipartParameterContentTypes; + } + + /** + * Obtain the multipart parameter content type Map for retrieval, + * lazily initializing it if necessary. + * @see #initializeMultipart() + */ + protected Map getMultipartParameterContentTypes() { + if (this.multipartParameterContentTypes == null) { + initializeMultipart(); + } + return this.multipartParameterContentTypes; + } + } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/MultipartFilter.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/MultipartFilter.java index c151aaa81b5..c7bff8e6e43 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/MultipartFilter.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/MultipartFilter.java @@ -1,5 +1,5 @@ /* - * 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 after this filter). * + *

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 web.xml. + * *

MultipartResolver lookup is customizable: Override this filter's * lookupMultipartResolver 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. - *

Default implementation delegates to the lookupMultipartResolver + *

The default implementation delegates to the lookupMultipartResolver * without arguments. * @return the MultipartResolver to use * @see #lookupMultipartResolver() @@ -140,11 +146,17 @@ public class MultipartFilter extends OncePerRequestFilter { * @return the MultipartResolver instance, or null 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); } } diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java new file mode 100644 index 00000000000..234f22b43c3 --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequest.java @@ -0,0 +1,94 @@ +/* + * 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.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.ServerHttpRequest; +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 extends ServletServerHttpRequest { + + private final MultipartHttpServletRequest multipartRequest; + + private final String partName; + + private final HttpHeaders headers; + + + /** + * 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(HttpServletRequest request, String partName) { + super(request); + + this.multipartRequest = (request instanceof MultipartHttpServletRequest ? + (MultipartHttpServletRequest) request : new StandardMultipartHttpServletRequest(request)); + this.partName = partName; + + this.headers = this.multipartRequest.getMultipartHeaders(this.partName); + if (this.headers == null) { + throw new IllegalArgumentException("No request part found for name '" + this.partName + "'"); + } + } + + + @Override + public HttpHeaders getHeaders() { + return this.headers; + } + + @Override + public InputStream getBody() throws IOException { + 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)); + } + } + } + +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java new file mode 100644 index 00000000000..6e7b7e641c3 --- /dev/null +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequest.java @@ -0,0 +1,175 @@ +/* + * 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 parts = request.getParts(); + MultiValueMap files = new LinkedMultiValueMap(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(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()); + } + } + +} diff --git a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java index 08b8a74492b..539c78c965f 100644 --- a/org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java +++ b/org.springframework.web/src/main/java/org/springframework/web/multipart/support/StandardServletMultipartResolver.java @@ -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 parts = request.getParts(); - MultiValueMap files = new LinkedMultiValueMap(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 headers = this.part.getHeaders(name); - return (headers != null ? StringUtils.toStringArray(headers) : null); - } - - public Iterator getHeaderNames() { - return this.part.getHeaderNames().iterator(); + LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex); } } diff --git a/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartFile.java b/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartFile.java index 6b87593a167..24518968c38 100644 --- a/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartFile.java +++ b/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartFile.java @@ -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 getHeaderNames() { - return Collections.singleton(CONTENT_TYPE).iterator(); - } - } diff --git a/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java b/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java index c0a7bc84c98..b01c4fdcf84 100644 --- a/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java +++ b/org.springframework.web/src/test/java/org/springframework/mock/web/MockMultipartHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * 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(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 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; + } + } + } diff --git a/org.springframework.web/src/test/java/org/springframework/web/multipart/RequestPartServletServerHttpRequestTests.java b/org.springframework.web/src/test/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequestTests.java similarity index 90% rename from org.springframework.web/src/test/java/org/springframework/web/multipart/RequestPartServletServerHttpRequestTests.java rename to org.springframework.web/src/test/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequestTests.java index 4835310cd29..a5364ebfaa4 100644 --- a/org.springframework.web/src/test/java/org/springframework/web/multipart/RequestPartServletServerHttpRequestTests.java +++ b/org.springframework.web/src/test/java/org/springframework/web/multipart/support/RequestPartServletServerHttpRequestTests.java @@ -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 {