From a1b2262ddad0cd43394aa97d2ecc1b3f37f9f1ad Mon Sep 17 00:00:00 2001 From: binchoo <079111w@gmail.com> Date: Thu, 13 Jan 2022 22:21:29 +0900 Subject: [PATCH 1/4] Replace MockMultipartFile with StandardMockMultipartFile --- ...ockMultipartHttpServletRequestBuilder.java | 50 ++++++++- .../standalone/MultipartControllerTests.java | 101 ++++++++++++++++-- 2 files changed, 141 insertions(+), 10 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java index 0ead22c22f5b..204d0cdc70d3 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Serializable; import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -154,7 +155,7 @@ protected final MockHttpServletRequest createServletRequest(ServletContext servl String filename = part.getSubmittedFileName(); InputStream is = part.getInputStream(); if (filename != null) { - request.addFile(new MockMultipartFile(name, filename, part.getContentType(), is)); + request.addFile(new MockStandardMultipartFile(part, filename)); } else { InputStreamReader reader = new InputStreamReader(is, getCharsetOrDefault(part, defaultCharset)); @@ -179,4 +180,51 @@ private Charset getCharsetOrDefault(Part part, Charset defaultCharset) { } return defaultCharset; } + + /** + * Spring MultipartFile adapter, wrapping a Servlet Part object. + */ + @SuppressWarnings("serial") + private static class MockStandardMultipartFile extends MockMultipartFile implements Part, Serializable { + + private final Part part; + + private final String filename; + + public MockStandardMultipartFile(Part part, String filename) throws IOException { + super(part.getName(), part.getInputStream()); + this.part = part; + this.filename = filename; + } + + @Override + public String getSubmittedFileName() { + return this.part.getSubmittedFileName(); + } + + @Override + public void write(String fileName) throws IOException { + this.part.write(fileName); + } + + @Override + public void delete() throws IOException { + this.part.delete(); + } + + @Override + public String getHeader(String name) { + return this.part.getHeader(name); + } + + @Override + public Collection getHeaders(String name) { + return this.part.getHeaders(name); + } + + @Override + public Collection getHeaderNames() { + return this.part.getHeaderNames(); + } + } } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java index e61d50f0d6e4..68c7e7f1d844 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java @@ -38,6 +38,7 @@ import org.springframework.stereotype.Controller; import org.springframework.test.web.servlet.MockMvc; import org.springframework.ui.Model; +import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -239,6 +240,38 @@ public void multipartRequestWithServletParts() throws Exception { .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); } + @Test + public void multipartRequestWithServletPartsForPartAttribute() throws Exception { + byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); + MockPart filePart = new MockPart("file", "orig", fileContent); + + byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); + MockPart jsonPart = new MockPart("json", json); + jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON); + + standaloneSetup(new MultipartController()).build() + .perform(multipart("/partattr").part(filePart).part(jsonPart)) + .andExpect(status().isFound()) + .andExpect(model().attribute("fileContent", fileContent)) + .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); + } + + @Test + public void multipartRequestWithServletPartsForMultipartFileAttribute() throws Exception { + byte[] fileContent = "foo".getBytes(StandardCharsets.UTF_8); + MockPart filePart = new MockPart("file", "orig", fileContent); + + byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); + MockPart jsonPart = new MockPart("json", json); + jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON); + + standaloneSetup(new MultipartController()).build() + .perform(multipart("/multipartfileattr").part(filePart).part(jsonPart)) + .andExpect(status().isFound()) + .andExpect(model().attribute("fileContent", fileContent)) + .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); + } + @Test // SPR-13317 public void multipartRequestWrapped() throws Exception { byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); @@ -341,29 +374,79 @@ public String processOptionalFileList(@RequestParam Optional return "redirect:/index"; } - @RequestMapping(value = "/part", method = RequestMethod.POST) - public String processPart(@RequestParam Part part, - @RequestPart Map json, Model model) throws IOException { + @RequestMapping(value = "/json", method = RequestMethod.POST) + public String processMultipart(@RequestPart Map json, Model model) { + model.addAttribute("json", json); + return "redirect:/index"; + } - model.addAttribute("fileContent", part.getInputStream()); - model.addAttribute("jsonContent", json); + @RequestMapping(value = "/partattr") + public String processPartAttribute(PartForm form, + @RequestPart(required = false) Map json, Model model) throws IOException { + + if (form != null) { + Part part = form.getFile(); + if (0 != part.getSize()) { + byte[] fileContent = StreamUtils.copyToByteArray(part.getInputStream()); + model.addAttribute("fileContent", fileContent); + } + } + if (json != null) { + model.addAttribute("jsonContent", json); + } return "redirect:/index"; } - @RequestMapping(value = "/json", method = RequestMethod.POST) - public String processMultipart(@RequestPart Map json, Model model) { - model.addAttribute("json", json); + @RequestMapping(value = "/multipartfileattr") + public String processMultipartFileAttribute(MultipartFileForm form, + @RequestPart(required = false) Map json, Model model) throws IOException { + + if (form != null) { + MultipartFile file = form.getFile(); + if (!file.isEmpty()) { + model.addAttribute("fileContent", file.getBytes()); + } + } + if (json != null) { + model.addAttribute("jsonContent", json); + } + return "redirect:/index"; } } + private static class PartForm { + + private Part file; + + public PartForm(Part file) { + this.file = file; + } + + public Part getFile() { + return file; + } + } + + private static class MultipartFileForm { + + private MultipartFile file; + + public MultipartFileForm(MultipartFile file) { + this.file = file; + } + + public MultipartFile getFile() { + return file; + } + } private static class RequestWrappingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { + FilterChain filterChain) throws IOException, ServletException { request = new HttpServletRequestWrapper(request); filterChain.doFilter(request, response); From ea1180be1d7a16749d6b2d97fc0a3c5772ff5914 Mon Sep 17 00:00:00 2001 From: binchoo <079111w@gmail.com> Date: Wed, 27 Apr 2022 00:57:33 +0900 Subject: [PATCH 2/4] Revert "Replace MockMultipartFile with StandardMockMultipartFile" This reverts commit a1b2262ddad0cd43394aa97d2ecc1b3f37f9f1ad. --- ...ockMultipartHttpServletRequestBuilder.java | 50 +-------- .../standalone/MultipartControllerTests.java | 101 ++---------------- 2 files changed, 10 insertions(+), 141 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java index 204d0cdc70d3..0ead22c22f5b 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java @@ -19,7 +19,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.Serializable; import java.net.URI; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -155,7 +154,7 @@ protected final MockHttpServletRequest createServletRequest(ServletContext servl String filename = part.getSubmittedFileName(); InputStream is = part.getInputStream(); if (filename != null) { - request.addFile(new MockStandardMultipartFile(part, filename)); + request.addFile(new MockMultipartFile(name, filename, part.getContentType(), is)); } else { InputStreamReader reader = new InputStreamReader(is, getCharsetOrDefault(part, defaultCharset)); @@ -180,51 +179,4 @@ private Charset getCharsetOrDefault(Part part, Charset defaultCharset) { } return defaultCharset; } - - /** - * Spring MultipartFile adapter, wrapping a Servlet Part object. - */ - @SuppressWarnings("serial") - private static class MockStandardMultipartFile extends MockMultipartFile implements Part, Serializable { - - private final Part part; - - private final String filename; - - public MockStandardMultipartFile(Part part, String filename) throws IOException { - super(part.getName(), part.getInputStream()); - this.part = part; - this.filename = filename; - } - - @Override - public String getSubmittedFileName() { - return this.part.getSubmittedFileName(); - } - - @Override - public void write(String fileName) throws IOException { - this.part.write(fileName); - } - - @Override - public void delete() throws IOException { - this.part.delete(); - } - - @Override - public String getHeader(String name) { - return this.part.getHeader(name); - } - - @Override - public Collection getHeaders(String name) { - return this.part.getHeaders(name); - } - - @Override - public Collection getHeaderNames() { - return this.part.getHeaderNames(); - } - } } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java index 68c7e7f1d844..e61d50f0d6e4 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java @@ -38,7 +38,6 @@ import org.springframework.stereotype.Controller; import org.springframework.test.web.servlet.MockMvc; import org.springframework.ui.Model; -import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -240,38 +239,6 @@ public void multipartRequestWithServletParts() throws Exception { .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); } - @Test - public void multipartRequestWithServletPartsForPartAttribute() throws Exception { - byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); - MockPart filePart = new MockPart("file", "orig", fileContent); - - byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); - MockPart jsonPart = new MockPart("json", json); - jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON); - - standaloneSetup(new MultipartController()).build() - .perform(multipart("/partattr").part(filePart).part(jsonPart)) - .andExpect(status().isFound()) - .andExpect(model().attribute("fileContent", fileContent)) - .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); - } - - @Test - public void multipartRequestWithServletPartsForMultipartFileAttribute() throws Exception { - byte[] fileContent = "foo".getBytes(StandardCharsets.UTF_8); - MockPart filePart = new MockPart("file", "orig", fileContent); - - byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); - MockPart jsonPart = new MockPart("json", json); - jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON); - - standaloneSetup(new MultipartController()).build() - .perform(multipart("/multipartfileattr").part(filePart).part(jsonPart)) - .andExpect(status().isFound()) - .andExpect(model().attribute("fileContent", fileContent)) - .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); - } - @Test // SPR-13317 public void multipartRequestWrapped() throws Exception { byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); @@ -374,79 +341,29 @@ public String processOptionalFileList(@RequestParam Optional return "redirect:/index"; } - @RequestMapping(value = "/json", method = RequestMethod.POST) - public String processMultipart(@RequestPart Map json, Model model) { - model.addAttribute("json", json); - return "redirect:/index"; - } - - @RequestMapping(value = "/partattr") - public String processPartAttribute(PartForm form, - @RequestPart(required = false) Map json, Model model) throws IOException { + @RequestMapping(value = "/part", method = RequestMethod.POST) + public String processPart(@RequestParam Part part, + @RequestPart Map json, Model model) throws IOException { - if (form != null) { - Part part = form.getFile(); - if (0 != part.getSize()) { - byte[] fileContent = StreamUtils.copyToByteArray(part.getInputStream()); - model.addAttribute("fileContent", fileContent); - } - } - if (json != null) { - model.addAttribute("jsonContent", json); - } + model.addAttribute("fileContent", part.getInputStream()); + model.addAttribute("jsonContent", json); return "redirect:/index"; } - @RequestMapping(value = "/multipartfileattr") - public String processMultipartFileAttribute(MultipartFileForm form, - @RequestPart(required = false) Map json, Model model) throws IOException { - - if (form != null) { - MultipartFile file = form.getFile(); - if (!file.isEmpty()) { - model.addAttribute("fileContent", file.getBytes()); - } - } - if (json != null) { - model.addAttribute("jsonContent", json); - } - + @RequestMapping(value = "/json", method = RequestMethod.POST) + public String processMultipart(@RequestPart Map json, Model model) { + model.addAttribute("json", json); return "redirect:/index"; } } - private static class PartForm { - - private Part file; - - public PartForm(Part file) { - this.file = file; - } - - public Part getFile() { - return file; - } - } - - private static class MultipartFileForm { - - private MultipartFile file; - - public MultipartFileForm(MultipartFile file) { - this.file = file; - } - - public MultipartFile getFile() { - return file; - } - } private static class RequestWrappingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, - FilterChain filterChain) throws IOException, ServletException { + FilterChain filterChain) throws IOException, ServletException { request = new HttpServletRequestWrapper(request); filterChain.doFilter(request, response); From 03e97377d76a32606af5c74286e6571f28e2efdf Mon Sep 17 00:00:00 2001 From: binchoo <079111w@gmail.com> Date: Wed, 27 Apr 2022 01:12:22 +0900 Subject: [PATCH 3/4] Improve javadoc of ServletRequestDataBinder and WebRequestDataBinder --- .../springframework/web/bind/ServletRequestDataBinder.java | 5 ++++- .../web/bind/support/WebRequestDataBinder.java | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java b/spring-web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java index db74e0020d3b..fd6ea6034e82 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java +++ b/spring-web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java @@ -95,11 +95,14 @@ public ServletRequestDataBinder(@Nullable Object target, String objectName) { * HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property, * invoking a "setUploadedFile" setter method. *

The type of the target property for a multipart file can be MultipartFile, - * byte[], or String. The latter two receive the contents of the uploaded file; + * Part, byte[], or String. The Part binding is only supported when the request + * is not a MultipartRequest. The latter two receive the contents of the uploaded file; * all metadata like original file name, content type, etc are lost in those cases. * @param request the request with parameters to bind (can be multipart) * @see org.springframework.web.multipart.MultipartHttpServletRequest + * @see org.springframework.web.multipart.MultipartRequest * @see org.springframework.web.multipart.MultipartFile + * @see jakarta.servlet.http.Part * @see #bind(org.springframework.beans.PropertyValues) */ public void bind(ServletRequest request) { diff --git a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java index 7be21a3db4ad..57f583de33c2 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java +++ b/spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java @@ -99,7 +99,8 @@ public WebRequestDataBinder(@Nullable Object target, String objectName) { * HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property, * invoking a "setUploadedFile" setter method. *

The type of the target property for a multipart file can be Part, MultipartFile, - * byte[], or String. The latter two receive the contents of the uploaded file; + * byte[], or String. The Part binding is only supported when the request + * is not a MultipartRequest. The latter two receive the contents of the uploaded file; * all metadata like original file name, content type, etc are lost in those cases. * @param request the request with parameters to bind (can be multipart) * @see org.springframework.web.multipart.MultipartRequest From 791999610e5a6028bfe8dfe44293beb3f267857c Mon Sep 17 00:00:00 2001 From: binchoo <079111w@gmail.com> Date: Wed, 27 Apr 2022 12:49:05 +0900 Subject: [PATCH 4/4] Verify multipart argument binding and property binding behaviors onto controller --- .../standalone/MultipartControllerTests.java | 109 +++++++++++++++++- 1 file changed, 106 insertions(+), 3 deletions(-) diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java index e61d50f0d6e4..96d0842065cc 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java @@ -38,6 +38,9 @@ import org.springframework.stereotype.Controller; import org.springframework.test.web.servlet.MockMvc; import org.springframework.ui.Model; +import org.springframework.util.StreamUtils; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -55,6 +58,7 @@ /** * @author Rossen Stoyanchev * @author Juergen Hoeller + * @author Jaebin Joo */ public class MultipartControllerTests { @@ -224,7 +228,7 @@ public void multipartRequestWithOptionalFileListNotPresent() throws Exception { } @Test - public void multipartRequestWithServletParts() throws Exception { + public void multipartRequestWithParts_resolvesMultipartFileArguments() throws Exception { byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); MockPart filePart = new MockPart("file", "orig", fileContent); @@ -239,6 +243,50 @@ public void multipartRequestWithServletParts() throws Exception { .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); } + @Test + public void multipartRequestWithParts_resolvesPartArguments() throws Exception { + byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); + MockPart filePart = new MockPart("file", "orig", fileContent); + + byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); + MockPart jsonPart = new MockPart("json", json); + jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON); + + standaloneSetup(new MultipartController()).build() + .perform(multipart("/part").part(filePart).part(jsonPart)) + .andExpect(status().isFound()) + .andExpect(model().attribute("fileContent", fileContent)) + .andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah"))); + } + + @Test + public void multipartRequestWithParts_resolvesMultipartFileProperties() throws Exception { + byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); + MockPart filePart = new MockPart("file", "orig", fileContent); + + standaloneSetup(new MultipartController()).build() + .perform(multipart("/multipartfileproperty").part(filePart)) + .andExpect(status().isFound()) + .andExpect(model().attribute("fileContent", fileContent)); + } + + @Test + public void multipartRequestWithParts_cannotResolvePartProperties() throws Exception { + byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8); + MockPart filePart = new MockPart("file", "orig", fileContent); + + Exception exception = standaloneSetup(new MultipartController()).build() + .perform(multipart("/partproperty").part(filePart)) + .andExpect(status().is4xxClientError()) + .andReturn() + .getResolvedException(); + + assertThat(exception).isNotNull(); + assertThat(exception).isInstanceOf(BindException.class); + assertThat(((BindException) exception).getFieldError("file")) + .as("MultipartRequest would not bind Part properties.").isNotNull(); + } + @Test // SPR-13317 public void multipartRequestWrapped() throws Exception { byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8); @@ -342,10 +390,13 @@ public String processOptionalFileList(@RequestParam Optional } @RequestMapping(value = "/part", method = RequestMethod.POST) - public String processPart(@RequestParam Part part, + public String processPart(@RequestPart Part file, @RequestPart Map json, Model model) throws IOException { - model.addAttribute("fileContent", part.getInputStream()); + if (file != null) { + byte[] content = StreamUtils.copyToByteArray(file.getInputStream()); + model.addAttribute("fileContent", content); + } model.addAttribute("jsonContent", json); return "redirect:/index"; @@ -356,8 +407,60 @@ public String processMultipart(@RequestPart Map json, Model mode model.addAttribute("json", json); return "redirect:/index"; } + + @RequestMapping(value = "/multipartfileproperty", method = RequestMethod.POST) + public String processMultipartFileBean(MultipartFileBean multipartFileBean, Model model, BindingResult bindingResult) + throws IOException { + + if (!bindingResult.hasErrors()) { + MultipartFile file = multipartFileBean.getFile(); + if (file != null) { + model.addAttribute("fileContent", file.getBytes()); + } + } + return "redirect:/index"; + } + + @RequestMapping(value = "/partproperty", method = RequestMethod.POST) + public String processPartBean(PartBean partBean, Model model, BindingResult bindingResult) + throws IOException { + + if (!bindingResult.hasErrors()) { + Part file = partBean.getFile(); + if (file != null) { + byte[] content = StreamUtils.copyToByteArray(file.getInputStream()); + model.addAttribute("fileContent", content); + } + } + return "redirect:/index"; + } } + private static class MultipartFileBean { + + private MultipartFile file; + + public MultipartFile getFile() { + return file; + } + + public void setFile(MultipartFile file) { + this.file = file; + } + } + + private static class PartBean { + + private Part file; + + public Part getFile() { + return file; + } + + public void setFile(Part file) { + this.file = file; + } + } private static class RequestWrappingFilter extends OncePerRequestFilter {