diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java
index 8e9c482c01d..db4722940a8 100644
--- a/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java
+++ b/spring-web/src/main/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolver.java
@@ -16,10 +16,14 @@
package org.springframework.web.method.annotation;
+import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.Part;
import org.springframework.core.MethodParameter;
+import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
@@ -29,22 +33,29 @@ import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.MultipartRequest;
+import org.springframework.web.multipart.support.MultipartResolutionDelegate;
/**
* Resolves {@link Map} method arguments annotated with an @{@link RequestParam}
* where the annotation does not specify a request parameter name.
- * See {@link RequestParamMethodArgumentResolver} for resolving {@link Map}
- * method arguments with a request parameter name.
*
- *
The created {@link Map} contains all request parameter name/value pairs.
- * If the method parameter type is {@link MultiValueMap} instead, the created
- * map contains all request parameters and all there values for cases where
- * request parameters have multiple values.
+ *
The created {@link Map} contains all request parameter name/value pairs,
+ * or all multipart files for a given parameter name if specifically declared
+ * with {@link MultipartFile} as the value type. If the method parameter type is
+ * {@link MultiValueMap} instead, the created map contains all request parameters
+ * and all their values for cases where request parameters have multiple values
+ * (or multiple multipart files of the same name).
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
+ * @author Juergen Hoeller
* @since 3.1
* @see RequestParamMethodArgumentResolver
+ * @see HttpServletRequest#getParameterMap()
+ * @see MultipartRequest#getMultiFileMap()
+ * @see MultipartRequest#getFileMap()
*/
public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
@@ -59,26 +70,71 @@ public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgum
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
- Class> paramType = parameter.getParameterType();
+ ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
- Map parameterMap = webRequest.getParameterMap();
- if (MultiValueMap.class.isAssignableFrom(paramType)) {
- MultiValueMap result = new LinkedMultiValueMap<>(parameterMap.size());
- parameterMap.forEach((key, values) -> {
- for (String value : values) {
- result.add(key, value);
+ if (MultiValueMap.class.isAssignableFrom(parameter.getParameterType())) {
+ // MultiValueMap
+ Class> valueType = resolvableType.as(MultiValueMap.class).getGeneric(1).resolve();
+ if (valueType == MultipartFile.class) {
+ MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
+ return (multipartRequest != null ? multipartRequest.getMultiFileMap() : new LinkedMultiValueMap<>(0));
+ }
+ else if (valueType == Part.class) {
+ HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
+ if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
+ Collection parts = servletRequest.getParts();
+ LinkedMultiValueMap result = new LinkedMultiValueMap<>(parts.size());
+ for (Part part : parts) {
+ result.add(part.getName(), part);
+ }
+ return result;
}
- });
- return result;
+ return new LinkedMultiValueMap<>(0);
+ }
+ else {
+ Map parameterMap = webRequest.getParameterMap();
+ MultiValueMap result = new LinkedMultiValueMap<>(parameterMap.size());
+ parameterMap.forEach((key, values) -> {
+ for (String value : values) {
+ result.add(key, value);
+ }
+ });
+ return result;
+ }
}
+
else {
- Map result = new LinkedHashMap<>(parameterMap.size());
- parameterMap.forEach((key, values) -> {
- if (values.length > 0) {
- result.put(key, values[0]);
+ // Regular Map
+ Class> valueType = resolvableType.asMap().getGeneric(1).resolve();
+ if (valueType == MultipartFile.class) {
+ MultipartRequest multipartRequest = MultipartResolutionDelegate.resolveMultipartRequest(webRequest);
+ return (multipartRequest != null ? multipartRequest.getFileMap() : new LinkedHashMap<>(0));
+ }
+ else if (valueType == Part.class) {
+ HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
+ if (servletRequest != null && MultipartResolutionDelegate.isMultipartRequest(servletRequest)) {
+ Collection parts = servletRequest.getParts();
+ LinkedHashMap result = new LinkedHashMap<>(parts.size());
+ for (Part part : parts) {
+ if (!result.containsKey(part.getName())) {
+ result.put(part.getName(), part);
+ }
+ }
+ return result;
}
- });
- return result;
+ return new LinkedHashMap<>(0);
+ }
+ else {
+ Map parameterMap = webRequest.getParameterMap();
+ Map result = new LinkedHashMap<>(parameterMap.size());
+ parameterMap.forEach((key, values) -> {
+ if (values.length > 0) {
+ result.put(key, values[0]);
+ }
+ });
+ return result;
+ }
}
}
+
}
diff --git a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java
index 9a32f1881fc..01b5481cc84 100644
--- a/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java
+++ b/spring-web/src/main/java/org/springframework/web/multipart/support/MultipartResolutionDelegate.java
@@ -25,8 +25,10 @@ import javax.servlet.http.Part;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
+import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
+import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.util.WebUtils;
/**
@@ -44,6 +46,19 @@ public abstract class MultipartResolutionDelegate {
public static final Object UNRESOLVABLE = new Object();
+ @Nullable
+ public static MultipartRequest resolveMultipartRequest(NativeWebRequest webRequest) {
+ MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
+ if (multipartRequest != null) {
+ return multipartRequest;
+ }
+ HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
+ if (servletRequest != null && isMultipartContent(servletRequest)) {
+ return new StandardMultipartHttpServletRequest(servletRequest);
+ }
+ return null;
+ }
+
public static boolean isMultipartRequest(HttpServletRequest request) {
return (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null ||
isMultipartContent(request));
@@ -103,13 +118,13 @@ public abstract class MultipartResolutionDelegate {
}
}
else if (Part.class == parameter.getNestedParameterType()) {
- return (isMultipart ? resolvePart(request, name) : null);
+ return (isMultipart ? request.getPart(name): null);
}
else if (isPartCollection(parameter)) {
return (isMultipart ? resolvePartList(request, name) : null);
}
else if (isPartArray(parameter)) {
- return (isMultipart ? resolvePartArray(request, name) : null);
+ return (isMultipart ? resolvePartList(request, name).toArray(new Part[0]) : null);
}
else {
return UNRESOLVABLE;
@@ -144,12 +159,8 @@ public abstract class MultipartResolutionDelegate {
return null;
}
- private static Part resolvePart(HttpServletRequest servletRequest, String name) throws Exception {
- return servletRequest.getPart(name);
- }
-
- private static List resolvePartList(HttpServletRequest servletRequest, String name) throws Exception {
- Collection parts = servletRequest.getParts();
+ private static List resolvePartList(HttpServletRequest request, String name) throws Exception {
+ Collection parts = request.getParts();
List result = new ArrayList<>(parts.size());
for (Part part : parts) {
if (part.getName().equals(name)) {
@@ -159,15 +170,4 @@ public abstract class MultipartResolutionDelegate {
return result;
}
- private static Part[] resolvePartArray(HttpServletRequest servletRequest, String name) throws Exception {
- Collection parts = servletRequest.getParts();
- List result = new ArrayList<>(parts.size());
- for (Part part : parts) {
- if (part.getName().equals(name)) {
- result.add(part);
- }
- }
- return result.toArray(new Part[0]);
- }
-
}
diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java
index 33b677af194..12b457d1ca8 100644
--- a/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java
+++ b/spring-web/src/test/java/org/springframework/web/method/annotation/RequestParamMapMethodArgumentResolverTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2018 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,74 +18,68 @@ package org.springframework.web.method.annotation;
import java.util.Collections;
import java.util.Map;
+import javax.servlet.http.Part;
-import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
+import org.springframework.mock.web.test.MockMultipartFile;
+import org.springframework.mock.web.test.MockMultipartHttpServletRequest;
+import org.springframework.mock.web.test.MockPart;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.ResolvableMethod;
+import org.springframework.web.multipart.MultipartFile;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.springframework.web.method.MvcAnnotationPredicates.requestParam;
+import static org.junit.Assert.*;
+import static org.springframework.web.method.MvcAnnotationPredicates.*;
/**
* Test fixture with {@link RequestParamMapMethodArgumentResolver}.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
+ * @author Juergen Hoeller
*/
public class RequestParamMapMethodArgumentResolverTests {
- private RequestParamMapMethodArgumentResolver resolver;
+ private RequestParamMapMethodArgumentResolver resolver = new RequestParamMapMethodArgumentResolver();
- private NativeWebRequest webRequest;
+ private MockHttpServletRequest request = new MockHttpServletRequest();
- private MockHttpServletRequest request;
+ private NativeWebRequest webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
private ResolvableMethod testMethod = ResolvableMethod.on(getClass()).named("handle").build();
- @Before
- public void setUp() throws Exception {
- resolver = new RequestParamMapMethodArgumentResolver();
-
- request = new MockHttpServletRequest();
- webRequest = new ServletWebRequest(request, new MockHttpServletResponse());
- }
-
-
@Test
public void supportsParameter() {
- MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class);
+ MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, String.class);
assertTrue(resolver.supportsParameter(param));
- param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class);
+ param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class, String.class, String.class);
assertTrue(resolver.supportsParameter(param));
- param = this.testMethod.annot(requestParam().name("name")).arg(Map.class);
+ param = this.testMethod.annot(requestParam().name("name")).arg(Map.class, String.class, String.class);
assertFalse(resolver.supportsParameter(param));
- param = this.testMethod.annotNotPresent(RequestParam.class).arg(Map.class);
+ param = this.testMethod.annotNotPresent(RequestParam.class).arg(Map.class, String.class, String.class);
assertFalse(resolver.supportsParameter(param));
}
@Test
- public void resolveMapArgument() throws Exception {
+ public void resolveMapOfString() throws Exception {
String name = "foo";
String value = "bar";
request.addParameter(name, value);
Map expected = Collections.singletonMap(name, value);
- MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class);
+ MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof Map);
@@ -93,7 +87,7 @@ public class RequestParamMapMethodArgumentResolverTests {
}
@Test
- public void resolveMultiValueMapArgument() throws Exception {
+ public void resolveMultiValueMapOfString() throws Exception {
String name = "foo";
String value1 = "bar";
String value2 = "baz";
@@ -103,19 +97,115 @@ public class RequestParamMapMethodArgumentResolverTests {
expected.add(name, value1);
expected.add(name, value2);
- MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class);
+ MethodParameter param = this.testMethod.annotPresent(RequestParam.class).arg(MultiValueMap.class, String.class, String.class);
Object result = resolver.resolveArgument(param, null, webRequest, null);
assertTrue(result instanceof MultiValueMap);
assertEquals("Invalid result", expected, result);
}
+ @Test
+ @SuppressWarnings("unchecked")
+ public void resolveMapOfMultipartFile() throws Exception {
+ MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
+ MultipartFile expected1 = new MockMultipartFile("mfile", "Hello World".getBytes());
+ MultipartFile expected2 = new MockMultipartFile("other", "Hello World 3".getBytes());
+ request.addFile(expected1);
+ request.addFile(expected2);
+ webRequest = new ServletWebRequest(request);
+
+ MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, MultipartFile.class);
+ Object result = resolver.resolveArgument(param, null, webRequest, null);
+
+ assertTrue(result instanceof Map);
+ Map resultMap = (Map) result;
+ assertEquals(2, resultMap.size());
+ assertEquals(expected1, resultMap.get("mfile"));
+ assertEquals(expected2, resultMap.get("other"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void resolveMultiValueMapOfMultipartFile() throws Exception {
+ MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
+ MultipartFile expected1 = new MockMultipartFile("mfilelist", "Hello World 1".getBytes());
+ MultipartFile expected2 = new MockMultipartFile("mfilelist", "Hello World 2".getBytes());
+ MultipartFile expected3 = new MockMultipartFile("other", "Hello World 3".getBytes());
+ request.addFile(expected1);
+ request.addFile(expected2);
+ request.addFile(expected3);
+ webRequest = new ServletWebRequest(request);
+
+ MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(MultiValueMap.class, String.class, MultipartFile.class);
+ Object result = resolver.resolveArgument(param, null, webRequest, null);
+
+ assertTrue(result instanceof MultiValueMap);
+ MultiValueMap resultMap = (MultiValueMap) result;
+ assertEquals(2, resultMap.size());
+ assertEquals(2, resultMap.get("mfilelist").size());
+ assertEquals(expected1, resultMap.get("mfilelist").get(0));
+ assertEquals(expected2, resultMap.get("mfilelist").get(1));
+ assertEquals(1, resultMap.get("other").size());
+ assertEquals(expected3, resultMap.get("other").get(0));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void resolveMapOfPart() throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setContentType("multipart/form-data");
+ Part expected1 = new MockPart("mfile", "Hello World".getBytes());
+ Part expected2 = new MockPart("other", "Hello World 3".getBytes());
+ request.addPart(expected1);
+ request.addPart(expected2);
+ webRequest = new ServletWebRequest(request);
+
+ MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(Map.class, String.class, Part.class);
+ Object result = resolver.resolveArgument(param, null, webRequest, null);
+
+ assertTrue(result instanceof Map);
+ Map resultMap = (Map) result;
+ assertEquals(2, resultMap.size());
+ assertEquals(expected1, resultMap.get("mfile"));
+ assertEquals(expected2, resultMap.get("other"));
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void resolveMultiValueMapOfPart() throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.setContentType("multipart/form-data");
+ Part expected1 = new MockPart("mfilelist", "Hello World 1".getBytes());
+ Part expected2 = new MockPart("mfilelist", "Hello World 2".getBytes());
+ Part expected3 = new MockPart("other", "Hello World 3".getBytes());
+ request.addPart(expected1);
+ request.addPart(expected2);
+ request.addPart(expected3);
+ webRequest = new ServletWebRequest(request);
+
+ MethodParameter param = this.testMethod.annot(requestParam().noName()).arg(MultiValueMap.class, String.class, Part.class);
+ Object result = resolver.resolveArgument(param, null, webRequest, null);
+
+ assertTrue(result instanceof MultiValueMap);
+ MultiValueMap resultMap = (MultiValueMap) result;
+ assertEquals(2, resultMap.size());
+ assertEquals(2, resultMap.get("mfilelist").size());
+ assertEquals(expected1, resultMap.get("mfilelist").get(0));
+ assertEquals(expected2, resultMap.get("mfilelist").get(1));
+ assertEquals(1, resultMap.get("other").size());
+ assertEquals(expected3, resultMap.get("other").get(0));
+ }
+
public void handle(
- @RequestParam Map, ?> param1,
- @RequestParam MultiValueMap, ?> param2,
- @RequestParam("name") Map, ?> param3,
- Map, ?> param4) {
+ @RequestParam Map param1,
+ @RequestParam MultiValueMap param2,
+ @RequestParam Map param3,
+ @RequestParam MultiValueMap param4,
+ @RequestParam Map param5,
+ @RequestParam MultiValueMap param6,
+ @RequestParam("name") Map param7,
+ Map param8) {
}
}
diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc
index 9d6f5026b4f..07da1850d41 100644
--- a/src/docs/asciidoc/web/webmvc.adoc
+++ b/src/docs/asciidoc/web/webmvc.adoc
@@ -1790,10 +1790,11 @@ and others) and is equivalent to `required=false`.
| For access to name-value pairs in URI path segments. See <>.
| `@RequestParam`
-| For access to the Servlet request parameters. Parameter values are converted to the declared
- method argument type. See <>.
+| For access to the Servlet request parameters, including multipart files. Parameter values
+ are converted to the declared method argument type. See <> as well
+ as <>.
- Note that use of `@RequestParam` is optional (for example, to set its attributes).
+ Note that use of `@RequestParam` is optional for simple parameter values.
See "`Any other argument`", at the end of this table.
| `@RequestHeader`
@@ -1809,12 +1810,12 @@ and others) and is equivalent to `required=false`.
argument type by using `HttpMessageConverter` implementations. See <>.
| `HttpEntity`
-| For access to request headers and body. The body is converted with `HttpMessageConverter` implementations.
+| For access to request headers and body. The body is converted with an `HttpMessageConverter`.
See <>.
| `@RequestPart`
-| For access to a part in a `multipart/form-data` request.
- See <>.
+| For access to a part in a `multipart/form-data` request, converting the part's body
+ with an `HttpMessageConverter`. See <>.
| `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap`
| For access to the model that is used in HTML controllers and exposed to templates as
@@ -2073,8 +2074,8 @@ To get all matrix variables, you can use a `MultiValueMap`, as the following exa
----
====
-Note that you need to enable the use of matrix variables. In the MVC Java configuration, you need
-to set a `UrlPathHelper` with `removeSemicolonContent=false` through
+Note that you need to enable the use of matrix variables. In the MVC Java configuration,
+you need to set a `UrlPathHelper` with `removeSemicolonContent=false` through
<>. In the MVC XML namespace, you can set
``.
@@ -2084,8 +2085,8 @@ to set a `UrlPathHelper` with `removeSemicolonContent=false` through
==== `@RequestParam`
[.small]#<>#
-You can use the `@RequestParam` annotation to bind Servlet request parameters (that is, query
-parameters or form data) to a method argument in a controller.
+You can use the `@RequestParam` annotation to bind Servlet request parameters (that is,
+query parameters or form data) to a method argument in a controller.
The following example shows how to do so:
@@ -2114,17 +2115,20 @@ The following example shows how to do so:
====
By default, method parameters that use this annotation are required, but you can specify that
-a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to `false`
-or by declaring the argument with an `java.util.Optional` wrapper.
+a method parameter is optional by setting the `@RequestParam` annotation's `required` flag to
+`false` or by declaring the argument with an `java.util.Optional` wrapper.
Type conversion is automatically applied if the target method parameter type is not
`String`. See <>.
+Declaring the argument type as an array or list allows for resolving multiple parameter
+values for the same parameter name.
+
When an `@RequestParam` annotation is declared as a `Map` or
-`MultiValueMap` argument, the map is populated with all request
-parameters.
+`MultiValueMap`, without a parameter name specified in the annotation,
+then the map is populated with the request parameter values for each given parameter name.
-Note that use of `@RequestParam` is optional, (for example, to set its attributes).
+Note that use of `@RequestParam` is optional (for example, to set its attributes).
By default, any argument that is a simple value type (as determined by
{api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty])
and is not resolved by any other argument resolver, is treated as if it were annotated
@@ -2534,8 +2538,8 @@ after the redirect, attributes from the "`input`" `FlashMap` are automatically a
.Matching requests to flash attributes
****
-The concept of flash attributes exists in many other web frameworks and has proven to sometimes be
-exposed to concurrency issues. This is because, by definition, flash attributes
+The concept of flash attributes exists in many other web frameworks and has proven to sometimes
+be exposed to concurrency issues. This is because, by definition, flash attributes
are to be stored until the next request. However the very "`next`" request may not be the
intended recipient but another asynchronous request (for example, polling or resource requests),
in which case the flash attributes are removed too early.
@@ -2577,16 +2581,21 @@ public class FileUploadController {
// store the bytes somewhere
return "redirect:uploadSuccess";
}
-
return "redirect:uploadFailure";
}
-
}
----
====
-NOTE: When you use Servlet 3.0 multipart parsing, you can also use `javax.servlet.http.Part`,
-instead of Spring's `MultipartFile`, as a method argument
+Declaring the argument type as a `List` allows for resolving multiple
+files for the same parameter name.
+
+When the `@RequestParam` annotation is declared as a `Map` or
+`MultiValueMap`, without a parameter name specified in the annotation,
+then the map is populated with the multipart files for each given parameter name.
+
+NOTE: With Servlet 3.0 multipart parsing, you may also declare `javax.servlet.http.Part`
+instead of Spring's `MultipartFile`, as a method argument or collection value type.
You can also use multipart content as part of data binding to a
<>. For example, the form field
@@ -2604,7 +2613,6 @@ class MyForm {
private MultipartFile file;
// ...
-
}
@Controller
@@ -2612,16 +2620,13 @@ public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
-
if (!form.getFile().isEmpty()) {
byte[] bytes = form.getFile().getBytes();
// store the bytes somewhere
return "redirect:uploadSuccess";
}
-
return "redirect:uploadFailure";
}
-
}
----
====