Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Post multipart/form-data for a File Upload using SpringMVC and MockMVC

I've created a photo uploader that works great using javax.ws.rs. Here's the signature and basic gist of it:

@POST
@Path("/upload/photo")
@Consumes("multipart/form-data")
@Produces("application/json")
public String uploadPhoto(InputStream stream){
        try {
            int read = 0;
            FileOutputStream fos = new FileOutputStream(file);
            CountingOutputStream out = new CountingOutputStream(fos);
            byte[] bytes = new byte[MAX_UPLOAD_SIZE];

            while ((read = stream.read(bytes)) != -1) {
                out.write(bytes, 0, read);
            }
            out.flush();
            out.close();
        } catch (IOException e) {
            // TODO throw!
            e.printStackTrace();
        }
    //...
}

I can test this using apache.commons.httpClient library like this:

    @Test
    public void testUpload() {

        int statusCode = 0;
        String methodResult = null;

        String endpoint = SERVICE_HOST + "/upload/photo";

        PostMethod post = new PostMethod(endpoint);

        File file = new File("/home/me/Desktop/someFolder/image.jpg");

        FileRequestEntity entity = new FileRequestEntity(file, "multipart/form-data");

        post.setRequestEntity(entity);

        try {
            httpClient.executeMethod(post);
            methodResult = post.getResponseBodyAsString();
        } catch (HttpException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        statusCode = post.getStatusCode();

        post.releaseConnection();
            //...
    }

This works great! The problem is that the rest of the application is written using Spring MVC. When I use Spring Mock MVC testing framework the program just hangs (shown in the code snippet below this one). Here is the SpringMVC code for the uploader:

@ResponseBody
@RequestMapping(    produces="application/json",
                    consumes="multipart/form-data",
                    method=RequestMethod.POST,
                    value="/photo")
public String uploadPhoto(@RequestPart("file") MultipartFile multipartFile){

            try {
                int read = 0;
                FileOutputStream fos = new FileOutputStream(file);
                CountingOutputStream out = new CountingOutputStream(fos);
                byte[] bytes = new byte[MAX_UPLOAD_SIZE];

                while ((read = multipartFile.getInputStream().read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }

                out.flush();
                out.close();

            } catch (IOException e) {
                // TODO throw!
                e.printStackTrace();
            }
            //...
}

And below is what I've implemented for testing, using Spring Mock MVC. I think the problem has to do with using fileUpload(...). Is there a way to test using the normal post(..) instead, like I can with apache? I'd prefer to use an InputStream as the argument and avoid using a MultipartFile.

@Test
public void testUpload() throws Exception {

    String endpoint = BASE_URL + "/upload/photo";

    FileInputStream fis = new FileInputStream("/home/me/Desktop/someFolder/image.jpg");
    MockMultipartFile multipartFile = new MockMultipartFile("file", fis);

    mockMvc.perform(fileUpload(endpoint)
            .file(multipartFile)
            .contentType(MediaType.MULTIPART_FORM_DATA))
            .andExpect(status().isOk());

}

Ideally, I'd like to use Spring MVC and the Spring Mock MVC framework, but the code I've provided just hangs on the while statement. Is what I'm doing correct as far as using the fileUpload method in the Spring test? Any advice is appreciated.

like image 803
Matt Avatar asked Dec 05 '13 01:12

Matt


People also ask

How do you send a file using multipart form data?

Follow this rules when creating a multipart form: Specify enctype="multipart/form-data" attribute on a form tag. Add a name attribute to a single input type="file" tag. DO NOT add a name attribute to any other input, select or textarea tags.

How do you send the multipart file and JSON data to spring boot?

To pass the Json and Multipart in the POST method we need to mention our content type in the consume part. And we need to pass the given parameter as User and Multipart file. Here, make sure we can pass only String + file not POJO + file. Then convert the String to Json using ObjectMapper in Service layer.

How do I pass a multipart file in JUnit?

FileInputStream inputFile = new FileInputStream( "path of the file"); MockMultipartFile file = new MockMultipartFile("file", "NameOfTheFile", "multipart/form-data", inputFile); now use the file input as multipart file.


2 Answers

By referring to the document, the code below can be more simple.

@Test
public void testFileUpload() throws Exception {
    FileInputStream input = new FileInputStream("/Downloads/WX.png");
    MockMultipartFile file = new MockMultipartFile(
            "image",
            "[email protected]",
            "image/png",
            input);
    this.mockMvc
            .perform(
                multipart("/api/note/image/create")
                        .file(file)
                        .header("Authorization", "BearereyJhbGciOiJIUzUxMiJ9")
            );
}
like image 108
xiaofeig Avatar answered Oct 07 '22 00:10

xiaofeig


  1. To add content to a mock post request use content(bytes[])
  2. media type parameter boundary was necessary

Also, it was safe to use a plain old InputStream from java.io as a parameter for the upload method, and still use MockMultipartFile in the request.

@Test
public void testUpload() throws Exception {

            String endpoint = BASE_URL + "/upload/photo";

            FileInputStream fis = new FileInputStream("/home/me/Desktop/someDir/image.jpg");
            MockMultipartFile multipartFile = new MockMultipartFile("file", fis);

            HashMap<String, String> contentTypeParams = new HashMap<String, String>();
            contentTypeParams.put("boundary", "265001916915724");
            MediaType mediaType = new MediaType("multipart", "form-data", contentTypeParams);

            mockMvc.perform(
                    post(endpoint)
                    .content(multipartFile.getBytes())
                    .contentType(mediaType))
                    .andExpect(status().isOk());
}
like image 38
Matt Avatar answered Oct 07 '22 00:10

Matt