3

I wanted to build a simple Rest api using Spring boot which accepts any given file and then performs some operations on it . I went through the spring examples on multipartFile https://spring.io/guides/gs/uploading-files/ and I decided to follow the same approach. The files that will be uploaded through my rest api will have some specific extension. So,i gave the content-type as application/octet-stream . When I try to run my unit test cases for the same, I always get the exception of

nested exception is org.springframework.web.multipart.MultipartException: The current request is not a multipart request

This exception does not appear if the content type is text/plain or if there is no 'consumes' parameter in the requestMapping.

My controller code looks as follows :

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

@Controller
@RequestMapping("/v1/sample")
public class SampleController {

    private static Logger log = LoggerFactory.getLogger(SampleController.class);

    @RequestMapping(path = "/{id}/upload",
                    consumes = {MediaType.APPLICATION_OCTET_STREAM_VALUE},
                    method = RequestMethod.POST)
    public ResponseEntity<String> uploadfile(@PathVariable String id,
            @RequestParam("file") MultipartFile upgradeFile) {
        log.info("Obtained a upload request for the id {}",id );
        return new ResponseEntity<String>("file upload has been accepted.",
                HttpStatus.ACCEPTED);
    }

}

And my unit test snippet is as follows :

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.fileUpload;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

import com.stellapps.devicemanager.fota.Application;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = Application.class)
@EnableWebMvc
public class ControllerTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    @Before
    public void mockMvcBuilder() {
        this.mockMvc = webAppContextSetup(webApplicationContext).build();
    }

    @Test
        public void test_ValidPayload() {
            String uri = "/v1/sample/1234/upload";
            Path path = Paths.get("src/test/resources/someFile");
            try {
                byte[] bytes = Files.readAllBytes(path);
                 MockMultipartFile multipartFile =
                            new MockMultipartFile("file", "someFile.diff", "application/octet-stream", bytes);
                 mockMvc.perform(fileUpload(uri).file(multipartFile).contentType(MediaType.APPLICATION_OCTET_STREAM_VALUE))
                    .andExpect(status().isAccepted());
            } catch (IOException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
}

If I use text/plain as the content-type and i give a normal text file, it goes through fine. If I add the content-type as application/octet-stream it throws the following exception

    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.springframework.web.multipart.MultipartException: The current request is not a multipart request
    at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.assertIsMultipartRequest(RequestPartMethodArgumentResolver.java:204)
    at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:129)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:99)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:817)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:731)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:968)

How Do I make my request accept application/octet-stream and what changes should I make to the test case to ensure it succeeds.

UPDATE:

Removing the consumes header and by not specifying the content-type in MockPartFile is one of the way to upload a file. I suppose by default the controller takes it as application/octet-stream

UPDATE:

Thank you for the response. I was using an earlier version of spring (1.3.1) and after going through the answer, I updated my spring version and the test case to match it and It started working.

Raveesh Sharma
  • 1,486
  • 5
  • 21
  • 38

1 Answers1

1

Try below steps

  • Remove the consumes from the RequestMapping attributes.
  • MockMultipartFile multipartFile = new MockMultipartFile("file","somename","multipart/form-data", fileinputstream);
  • Change to MockMvc:

    mockMvc.perform(MockMvcRequestBuilders.fileUpload("/v1/sample/1234/payload")                
                .file(multipartFile)
                .andExpect(status().isOk());
    

For help check Upload file using Spring mvc and MockMVC

EDIT:

I've tried a sample spring-boot-rest app with your scenario. Seems application/octet-stream is no issue. Please check my repo https://github.com/satya-j/boot-file-manager

Javier C.
  • 7,859
  • 5
  • 41
  • 53
satya-j
  • 349
  • 1
  • 4
  • 11
  • What difference will this make? What was wrong previously and how do these changes fix it? – Sotirios Delimanolis Oct 26 '16 at 15:42
  • My suggestion to the question is based on the file uploading mechanisms I have been using. All I can do is point at [link](http://stackoverflow.com/a/4526286/7063373) [link]http://stackoverflow.com/a/33033540/7063373 . The scenario for most file uploading is submit through a form with some additional data , so multipart/form-data – satya-j Oct 26 '16 at 17:39
  • If you think a post is a duplicate, flag to close it as such. Your answer currently inspires no confidence. You haven't given any explanation as to why their code/configuration/whatever doesn't work and haven't explained why your suggestions will make it work. It's a poor answer. – Sotirios Delimanolis Oct 26 '16 at 17:49
  • @SotiriosDelimanolis I agree, I'm new here. wasn't aware of flagging and haven't explored fully. Thanks for pointing it out. Would you suggest me to delete my answer ? – satya-j Oct 26 '16 at 17:52
  • First, I would suggest you improve it if you can. See [How do I write a good answer?](http://stackoverflow.com/help/how-to-answer). If you can't/don't know, then, sure, delete it. – Sotirios Delimanolis Oct 26 '16 at 18:05
  • Thank you for the answer. But I want to emphasize that file upload need not always take place from a User interface. It can be an API call as well . In this scenario @RequestParam("file") is actually bound to a web request parameter. but ideally, I do not wish to have the @RequestParam but rather use Multipart / some alternative to read the file with the content-type as application/octet-stream – Raveesh Sharma Oct 27 '16 at 05:08
  • Thank you for the response. I was using an earlier version of spring (1.3.1) and after going through the answer, I updated my spring version and the test case to match it and It started working. – Raveesh Sharma Oct 27 '16 at 08:43