Testing file upload in Spring Boot leads to FileUploadException (multipart boundary was not set)

Viewed 262

I'm trying to upload files to my Spring Boot application and directly writing them to their destination (not in a temp file first). The application code I have works, but I can't get my unit test to work. My controller looks like this:

    @PostMapping("/upload")
    @ResponseBody
    public String handleFileUpload(final HttpServletRequest request) throws IOException {
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        if (!isMultipart) {
            throw new ResponseStatusException(BAD_REQUEST, "Input was not of type multipart");
        }

        ServletFileUpload upload = new ServletFileUpload();
        FileItemIterator fileIterator = upload.getItemIterator(request);
        while (fileIterator.hasNext()) {
            FileItemStream item = fileIterator.next();
            if (!item.isFormField()) {
                // Save the file
                try {
                    return myFileStorageService.store(item.openStream());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        throw new ResponseStatusException(BAD_REQUEST, "Input did not contain a file");
    }

This code works great, but my test doesn't:

    @MockBean
    private MyFileStorageService myFileStorageService;
    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldUploadFile() throws Exception {
        final InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("testfile.txt");
        final MockMultipartFile testFile = new MockMultipartFile("file", "testfile.txt", null, inputStream);

        doReturn("success!").when(myFileStorageService).store(testFile);

        mockMvc.perform(multipart("/upload").file(testFile))
                .andExpect(status().isOk())
                .andExpect(content().string("success!"));

        verify(myFileStorageService).store(testFile);
    }

This results in the following exception:

org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found

    at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.init(FileItemIteratorImpl.java:189)
    at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.getMultiPartStream(FileItemIteratorImpl.java:205)
    at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.findNextItem(FileItemIteratorImpl.java:224)
    at org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl.<init>(FileItemIteratorImpl.java:142)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:252)
    at org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload.getItemIterator(ServletFileUpload.java:134)
    at com.lolmewn.FileUploadController.handleFileUpload(FileUploadController.java:128)
...

And in my config, I have configured the following:

spring:
  servlet:
    multipart:
      enabled: false
      max-file-size: -1
      max-request-size: -1

I expect Spring would generate the multipart boundaries for me, just like the browser or Postman do, is this not the case? I saw many similar questions, with most of them explicitly setting their content-type as the primary error, but as far as I know I'm not setting a content-type anywhere, so I expect Spring to generate it for me.

1 Answers

If you are using default application.properties, then add @SpringBootTest annotation at top of your class which will instantiate it. If using something like application-test.properties you need to include @ActiveProfiles(test) as well.
If you are using a config class to represent it

@EnableConfigurationProperties(value = YourConfig.class)

EDIT: Change

 final MockMultipartFile testFile = new MockMultipartFile("file", "testfile.txt", null, inputStream);

To

final MockMultipartFile testFile = new MockMultipartFile("file", "testfile.txt",  
MediaType.MULTIPART_FORM_DATA_VALUE, inputStream); 
Related