Skip to content

Commit a1b2262

Browse files
committed
Replace MockMultipartFile with StandardMockMultipartFile
1 parent ec9ce5b commit a1b2262

File tree

2 files changed

+141
-10
lines changed

2 files changed

+141
-10
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/request/MockMultipartHttpServletRequestBuilder.java

+49-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.IOException;
2020
import java.io.InputStream;
2121
import java.io.InputStreamReader;
22+
import java.io.Serializable;
2223
import java.net.URI;
2324
import java.nio.charset.Charset;
2425
import java.nio.charset.StandardCharsets;
@@ -154,7 +155,7 @@ protected final MockHttpServletRequest createServletRequest(ServletContext servl
154155
String filename = part.getSubmittedFileName();
155156
InputStream is = part.getInputStream();
156157
if (filename != null) {
157-
request.addFile(new MockMultipartFile(name, filename, part.getContentType(), is));
158+
request.addFile(new MockStandardMultipartFile(part, filename));
158159
}
159160
else {
160161
InputStreamReader reader = new InputStreamReader(is, getCharsetOrDefault(part, defaultCharset));
@@ -179,4 +180,51 @@ private Charset getCharsetOrDefault(Part part, Charset defaultCharset) {
179180
}
180181
return defaultCharset;
181182
}
183+
184+
/**
185+
* Spring MultipartFile adapter, wrapping a Servlet Part object.
186+
*/
187+
@SuppressWarnings("serial")
188+
private static class MockStandardMultipartFile extends MockMultipartFile implements Part, Serializable {
189+
190+
private final Part part;
191+
192+
private final String filename;
193+
194+
public MockStandardMultipartFile(Part part, String filename) throws IOException {
195+
super(part.getName(), part.getInputStream());
196+
this.part = part;
197+
this.filename = filename;
198+
}
199+
200+
@Override
201+
public String getSubmittedFileName() {
202+
return this.part.getSubmittedFileName();
203+
}
204+
205+
@Override
206+
public void write(String fileName) throws IOException {
207+
this.part.write(fileName);
208+
}
209+
210+
@Override
211+
public void delete() throws IOException {
212+
this.part.delete();
213+
}
214+
215+
@Override
216+
public String getHeader(String name) {
217+
return this.part.getHeader(name);
218+
}
219+
220+
@Override
221+
public Collection<String> getHeaders(String name) {
222+
return this.part.getHeaders(name);
223+
}
224+
225+
@Override
226+
public Collection<String> getHeaderNames() {
227+
return this.part.getHeaderNames();
228+
}
229+
}
182230
}

spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/MultipartControllerTests.java

+92-9
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.stereotype.Controller;
3939
import org.springframework.test.web.servlet.MockMvc;
4040
import org.springframework.ui.Model;
41+
import org.springframework.util.StreamUtils;
4142
import org.springframework.web.bind.annotation.RequestMapping;
4243
import org.springframework.web.bind.annotation.RequestMethod;
4344
import org.springframework.web.bind.annotation.RequestParam;
@@ -239,6 +240,38 @@ public void multipartRequestWithServletParts() throws Exception {
239240
.andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah")));
240241
}
241242

243+
@Test
244+
public void multipartRequestWithServletPartsForPartAttribute() throws Exception {
245+
byte[] fileContent = "bar".getBytes(StandardCharsets.UTF_8);
246+
MockPart filePart = new MockPart("file", "orig", fileContent);
247+
248+
byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8);
249+
MockPart jsonPart = new MockPart("json", json);
250+
jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON);
251+
252+
standaloneSetup(new MultipartController()).build()
253+
.perform(multipart("/partattr").part(filePart).part(jsonPart))
254+
.andExpect(status().isFound())
255+
.andExpect(model().attribute("fileContent", fileContent))
256+
.andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah")));
257+
}
258+
259+
@Test
260+
public void multipartRequestWithServletPartsForMultipartFileAttribute() throws Exception {
261+
byte[] fileContent = "foo".getBytes(StandardCharsets.UTF_8);
262+
MockPart filePart = new MockPart("file", "orig", fileContent);
263+
264+
byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8);
265+
MockPart jsonPart = new MockPart("json", json);
266+
jsonPart.getHeaders().setContentType(MediaType.APPLICATION_JSON);
267+
268+
standaloneSetup(new MultipartController()).build()
269+
.perform(multipart("/multipartfileattr").part(filePart).part(jsonPart))
270+
.andExpect(status().isFound())
271+
.andExpect(model().attribute("fileContent", fileContent))
272+
.andExpect(model().attribute("jsonContent", Collections.singletonMap("name", "yeeeah")));
273+
}
274+
242275
@Test // SPR-13317
243276
public void multipartRequestWrapped() throws Exception {
244277
byte[] json = "{\"name\":\"yeeeah\"}".getBytes(StandardCharsets.UTF_8);
@@ -341,29 +374,79 @@ public String processOptionalFileList(@RequestParam Optional<List<MultipartFile>
341374
return "redirect:/index";
342375
}
343376

344-
@RequestMapping(value = "/part", method = RequestMethod.POST)
345-
public String processPart(@RequestParam Part part,
346-
@RequestPart Map<String, String> json, Model model) throws IOException {
377+
@RequestMapping(value = "/json", method = RequestMethod.POST)
378+
public String processMultipart(@RequestPart Map<String, String> json, Model model) {
379+
model.addAttribute("json", json);
380+
return "redirect:/index";
381+
}
347382

348-
model.addAttribute("fileContent", part.getInputStream());
349-
model.addAttribute("jsonContent", json);
383+
@RequestMapping(value = "/partattr")
384+
public String processPartAttribute(PartForm form,
385+
@RequestPart(required = false) Map<String, String> json, Model model) throws IOException {
386+
387+
if (form != null) {
388+
Part part = form.getFile();
389+
if (0 != part.getSize()) {
390+
byte[] fileContent = StreamUtils.copyToByteArray(part.getInputStream());
391+
model.addAttribute("fileContent", fileContent);
392+
}
393+
}
394+
if (json != null) {
395+
model.addAttribute("jsonContent", json);
396+
}
350397

351398
return "redirect:/index";
352399
}
353400

354-
@RequestMapping(value = "/json", method = RequestMethod.POST)
355-
public String processMultipart(@RequestPart Map<String, String> json, Model model) {
356-
model.addAttribute("json", json);
401+
@RequestMapping(value = "/multipartfileattr")
402+
public String processMultipartFileAttribute(MultipartFileForm form,
403+
@RequestPart(required = false) Map<String, String> json, Model model) throws IOException {
404+
405+
if (form != null) {
406+
MultipartFile file = form.getFile();
407+
if (!file.isEmpty()) {
408+
model.addAttribute("fileContent", file.getBytes());
409+
}
410+
}
411+
if (json != null) {
412+
model.addAttribute("jsonContent", json);
413+
}
414+
357415
return "redirect:/index";
358416
}
359417
}
360418

419+
private static class PartForm {
420+
421+
private Part file;
422+
423+
public PartForm(Part file) {
424+
this.file = file;
425+
}
426+
427+
public Part getFile() {
428+
return file;
429+
}
430+
}
431+
432+
private static class MultipartFileForm {
433+
434+
private MultipartFile file;
435+
436+
public MultipartFileForm(MultipartFile file) {
437+
this.file = file;
438+
}
439+
440+
public MultipartFile getFile() {
441+
return file;
442+
}
443+
}
361444

362445
private static class RequestWrappingFilter extends OncePerRequestFilter {
363446

364447
@Override
365448
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
366-
FilterChain filterChain) throws IOException, ServletException {
449+
FilterChain filterChain) throws IOException, ServletException {
367450

368451
request = new HttpServletRequestWrapper(request);
369452
filterChain.doFilter(request, response);

0 commit comments

Comments
 (0)