Multipart Form Data File Uploading Made Simple with REST Assured

From a client perspective it has always seemed to me that uploading a large file or stream to a server using multi-part form data encoding in Java is overly complex. To address this I implemented support for it in REST Assured 1.3.

Example

Let’s say you have a simple HTML page that performs file-uploading from a browser like this:

<html>
<body>
	<form id="upload" action="/fileUpload" method="post" enctype="multipart/form-data">
		<input type="file" name="file" size="40">
		<input type=submit value="Upload!">
	</form>
</body>
</html>

We also assume that we have a server that receives the file and return a JSON response like this if everything was ok:

{ "fileUploadResult" : "OK" }

Now let’s use REST Assured to upload a file and assert that the upload was OK:

given().
         multiPart(new File("/home/johan/some_large_file.bin")).
expect().
         body("fileUploadResult", is("OK")).
when().
         post("/fileUpload");

With so little code you’ve now managed to POST a large file using multi-part form data encoding to the server and asserted that the expected JSON bundled in the response body was correct.

But how can this work? We haven’t even specified a control name? Well if you don’t define a control name specifically REST Assured will default to “file”. In many cases this is of course not the case. For example let’s say we change the control name in the HTML form to “file2” instead:

<html>
<body>
	<form id="upload" action="/fileUpload" method="post" enctype="multipart/form-data">
		<input type="file" name="file2" size="40">
		<input type=submit value="Upload!">
	</form>
</body>
</html>

REST Assured still makes it simple:

given().
         multiPart("file2", new File("/home/johan/some_large_file.bin")).
expect().
         body("fileUploadResult", is("OK")).
when().
         post("/fileUpload");

Multiple parts

You can even send multiple files, streams, byte-arrays and form parameters in the same request:

InputStream inputStream = ..

given().
         multiPart("file1", new File("/home/johan/some_large_file.bin")).
         multiPart("file2", new File("/home/johan/some_other_large_file.bin")).
         multiPart("file3", "file_name.bin", inputStream).
         formParam("name", "value").
expect().
         body("fileUploadResult", is("OK")).
when().
         post("/advancedFileUpload");

Summary

As you’ve hopefully seen it’s very simple do multi-part form data uploading with REST Assured. Credits should of course also go to Apache HTTP Client for making it possible. To get started with REST Assured today just visit the web-site. Enjoy!

This Post Has 11 Comments

  1. hi jay,

    i have payload as below, could you please help me sending the below payload as post request.

    {
    “createdBy”: “exchange@sdp.com”,
    “createdOn”: “2015-08-07T16:47:27.527+05:30”,
    “id”: 500050,
    “updatedBy”: “exchange@sdp.com”,
    “updatedOn”: “2015-08-11T12:35:29.606+05:30”,
    “accountContexts”: [
    “EXCHANGE”
    ],
    “accountReferenceId”: “C00000003”,
    “billPayer”: false,
    “changePassword”: false,
    “correspondenceAddress”: {
    “createdBy”: “exchange@sdp.com”,
    “createdOn”: “2015-08-07T16:47:27.528+05:30”,
    “id”: 500050,
    “updatedBy”: “exchange@sdp.com”,
    “updatedOn”: “2015-08-07T16:47:27.528+05:30”,
    “city”: “bangalore”,
    “country”: “india”,
    “postalCode”: “23167”,
    “state”: “KA”,
    “street”: “Sarjapur”
    },
    “emailAddress”: “operator111@cim.com”,
    “name”: “operator111@cim.com”,
    “parentInfo”: {
    “createdBy”: “superadmin”,
    “createdOn”: “2015-05-13T12:41:23.433+05:30”,
    “id”: 1011,
    “updatedBy”: “superadmin”,
    “updatedOn”: “2015-05-13T12:41:23.433+05:30”,
    “accountContexts”: [
    “EXCHANGE”
    ],
    “accountReferenceId”: “A1011”,
    “billPayer”: true,
    “emailAddress”: “exchange@sdp.com”,
    “name”: “EXCHANGE”,
    “phoneNumber”: “9181818181”,
    “representative”: false,
    “state”: “ACTIVE”,
    “typeId”: 1001,
    “typeName”: “EXCHANGE_ACCOUNT_TYPE”,
    “userName”: “exchange@sdp.com”
    },
    “phoneNumber”: “234567”,
    “representative”: false,
    “state”: “OBSOLETE”,
    “type”: {
    “createdBy”: “exchange@sdp.com”,
    “createdOn”: “2015-08-07T12:53:04.048+05:30”,
    “id”: 500000,
    “updatedBy”: “exchange@sdp.com”,
    “updatedOn”: “2015-08-07T12:53:04.048+05:30”,
    “name”: “CIM_USERS”,
    “ownerName”: “EXCHANGE”
    },
    “useExternalAuthentication”: false,
    “userName”: “operator111@cim.com”,
    “password”: “Cisco_123”
    }

    1. Please use the mailing list and I’ll try to help

  2. Helpful.. Thanks :)

  3. Hello Johan! I met a problem using this feature. In my service I wanna to upload a html file and the Java code is as following:

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity uploadDocument(@RequestPart(“file”) final MultipartFile file,
    final HttpServletRequest request) throws IOException {
    final String id = documentService.saveDocument(file);
    final String url = request.getScheme() + “://” + request.getServerName() + “:” + request.getServerPort() + “/documents/” + id;
    return ControllerUtil.resourceCreatedMessage(id, url);
    }

    Then I tried to test this function:

    given().multiPart(“file”, new File(path))
    .expect().statusCode(201)
    .when().post(HOST + “/documents”);

    but I got the error message:

    java.lang.AssertionError: 1 expectation failed.
    Expected status code doesn’t match actual status code .

    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
    at org.codehaus.groovy.reflection.CachedConstructor.doConstructorInvoke(CachedConstructor.java:77)
    at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrap.callConstructor(ConstructorSite.java:84)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
    at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure.validate(ResponseSpecificationImpl.groovy:451)
    at io.restassured.internal.ResponseSpecificationImpl$HamcrestAssertionClosure$validate$1.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at io.restassured.internal.RequestSpecificationImpl.applyPathParamsAndSendRequest(RequestSpecificationImpl.groovy:1632)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1215)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1024)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:812)
    at io.restassured.internal.RequestSpecificationImpl.invokeMethod(RequestSpecificationImpl.groovy)
    at org.codehaus.groovy.runtime.callsite.PogoInterceptableSite.call(PogoInterceptableSite.java:48)
    at org.codehaus.groovy.runtime.callsite.PogoInterceptableSite.callCurrent(PogoInterceptableSite.java:58)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:182)
    at io.restassured.internal.RequestSpecificationImpl.applyPathParamsAndSendRequest(RequestSpecificationImpl.groovy:1637)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1215)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1024)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:812)
    at io.restassured.internal.RequestSpecificationImpl.invokeMethod(RequestSpecificationImpl.groovy)
    at org.codehaus.groovy.runtime.callsite.PogoInterceptableSite.call(PogoInterceptableSite.java:48)
    at org.codehaus.groovy.runtime.callsite.PogoInterceptableSite.callCurrent(PogoInterceptableSite.java:58)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:182)
    at io.restassured.internal.RequestSpecificationImpl.post(RequestSpecificationImpl.groovy:170)
    at io.restassured.internal.RequestSpecificationImpl.post(RequestSpecificationImpl.groovy)
    at integrationTest.DocumentsIT.testDocuments(DocumentsIT.java:29)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

    Thank you so much!

  4. You are awesome. Thank you so much. This article helped me a lot. :)

  5. Hi
    I need help ,
    I have a multi part payload ( that has 5-6 part with zip file within the parts ) that I want to gzip the whole payload it and send it with rest assured .
    Can someone help understand the syntax ?
    How do I write it ?
    Thanks
    Please write mails and help me ….
    Odedkessler

  6. Hi,
    Just now sure how to give the location of the file which i want to include in the code.As of now i am giving the location of the file till the filename but getting 500 error.

  7. Hi,

    The file that I am uploading is becoming of zero bytes when being sent as multipart.

Leave a Reply

Close Menu