117

In the days of version 3.x of Apache Commons HttpClient, making a multipart/form-data POST request was possible (an example from 2004). Unfortunately this is no longer possible in version 4.0 of HttpClient.

For our core activity "HTTP", multipart is somewhat out of scope. We'd love to use multipart code maintained by some other project for which it is in scope, but I'm not aware of any. We tried to move the multipart code to commons-codec a few years ago, but I didn't take off there. Oleg recently mentioned another project that has multipart parsing code and might be interested in our multipart formatting code. I don't know the current status on that. (http://www.nabble.com/multipart-form-data-in-4.0-td14224819.html)

Is anybody aware of any Java library that allows me to write an HTTP client that can make a multipart/form-data POST request?

Background: I want to use the Remote API of Zoho Writer.

13 Answers13

164

We use HttpClient 4.x to make multipart file post.

UPDATE: As of HttpClient 4.3, some classes have been deprecated. Here is the code with new API:

CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost uploadFile = new HttpPost("...");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("field1", "yes", ContentType.TEXT_PLAIN);

// This attaches the file to the POST:
File f = new File("[/path/to/upload]");
builder.addBinaryBody(
    "file",
    new FileInputStream(f),
    ContentType.APPLICATION_OCTET_STREAM,
    f.getName()
);

HttpEntity multipart = builder.build();
uploadFile.setEntity(multipart);
CloseableHttpResponse response = httpClient.execute(uploadFile);
HttpEntity responseEntity = response.getEntity();

Below is the original snippet of code with deprecated HttpClient 4.0 API:

HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);

FileBody bin = new FileBody(new File(fileName));
StringBody comment = new StringBody("Filename: " + fileName);

MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("bin", bin);
reqEntity.addPart("comment", comment);
httppost.setEntity(reqEntity);

HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
G. Sylvie Davies
  • 5,049
  • 3
  • 21
  • 30
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
  • 71
    Ah, the multipart stuff has been moved to org.apache.httpcomponents-httpmime-4.0! Could be mentioned somewhere :/ –  Sep 04 '09 at 13:00
  • I tried your updated code which works fine with small files but does not work with large files. Can you help me with this [question](http://stackoverflow.com/questions/25850077/how-to-upload-large-files-by-multipart-request-in-java) – AabinGunz Sep 19 '14 at 05:25
  • Hi ZZ, I've made the above change in my code, however, I am facing a new issue now - my REST endpoint is not accepting the request. It's expecting the following parameters: ~@PathVariable final String id, @RequestParam("image") final MultipartFile image, @RequestParam("l") final String l, @RequestParam("lo") final String lo, @RequestParam("bac") final String bac, @RequestParam("cac") final String cac, @RequestParam("m") final String m ... Previously, the request was being accepted. But now I am getting 500 error. Any ideas why this might be happening? – Logan Jul 27 '15 at 10:59
  • I edited the answer so that the code example doesn't horizontal scroll anymore --- the scroll caused me to miss an important final parameter when I tried to use it in my own work. – G. Sylvie Davies Jul 14 '16 at 19:56
  • 1
    Here are the Maven dependecies for the updated answer org.apache.httpcomponents httpclient 4.3.6 org.apache.httpcomponents httpmime 4.3.6 – Wazime Nov 03 '17 at 16:47
  • hi, in this solution, can i define what is the chuck size, or the part size to upload? assume i have a requirement for example that is every part to be 5 MB – ibr Apr 26 '18 at 08:18
  • I got runtime error: `NoSuchMethodError: org.apache.http.entity.ContentType.create...` at MultipartEntityBuilder.buildEntity, at MultipartEntityBuilder. **build**. what's the reason? – Lei Yang May 16 '18 at 02:15
  • `org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(ClassicHttpRequest)` is deprecated in version 5. – Cardinal System Mar 07 '23 at 19:29
39

These are the Maven dependencies I have.

Java Code:

HttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);

FileBody uploadFilePart = new FileBody(uploadFile);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("upload-file", uploadFilePart);
httpPost.setEntity(reqEntity);

HttpResponse response = httpclient.execute(httpPost);

Maven Dependencies in pom.xml:

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.0.1</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpmime</artifactId>
  <version>4.0.1</version>
  <scope>compile</scope>
</dependency>
Jaco van Niekerk
  • 745
  • 1
  • 6
  • 11
21

If size of the JARs matters (e.g. in case of applet), one can also directly use httpmime with java.net.HttpURLConnection instead of HttpClient.

httpclient-4.2.4:      423KB
httpmime-4.2.4:         26KB
httpcore-4.2.4:        222KB
commons-codec-1.6:     228KB
commons-logging-1.1.1:  60KB
Sum:                   959KB

httpmime-4.2.4:         26KB
httpcore-4.2.4:        222KB
Sum:                   248KB

Code:

HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");

FileBody fileBody = new FileBody(new File(fileName));
MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.STRICT);
multipartEntity.addPart("file", fileBody);

connection.setRequestProperty("Content-Type", multipartEntity.getContentType().getValue());
OutputStream out = connection.getOutputStream();
try {
    multipartEntity.writeTo(out);
} finally {
    out.close();
}
int status = connection.getResponseCode();
...

Dependency in pom.xml:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.2.4</version>
</dependency>
anre
  • 3,617
  • 26
  • 33
  • FileBody where this came from? Is there a (easy) way to not use apace.httpcomponents ? – Jr. Jan 05 '16 at 19:13
11

Here's a solution that does not require any libraries.

This routine transmits every file in the directory d:/data/mpf10 to urlToConnect


String boundary = Long.toHexString(System.currentTimeMillis());
URLConnection connection = new URL(urlToConnect).openConnection();
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
PrintWriter writer = null;
try {
    writer = new PrintWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8"));
    File dir = new File("d:/data/mpf10");
    for (File file : dir.listFiles()) {
        if (file.isDirectory()) {
            continue;
        }
        writer.println("--" + boundary);
        writer.println("Content-Disposition: form-data; name=\"" + file.getName() + "\"; filename=\"" + file.getName() + "\"");
        writer.println("Content-Type: text/plain; charset=UTF-8");
        writer.println();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
            for (String line; (line = reader.readLine()) != null;) {
                writer.println(line);
            }
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
    }
    writer.println("--" + boundary + "--");
} finally {
    if (writer != null) writer.close();
}
// Connection is lazily executed whenever you request any status.
int responseCode = ((HttpURLConnection) connection).getResponseCode();
// Handle response
user1005939
  • 123
  • 1
  • 10
8

Use this code to upload images or any other files to the server using post in multipart.

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;

public class SimplePostRequestTest {

    public static void main(String[] args) throws UnsupportedEncodingException, IOException {
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("http://192.168.0.102/uploadtest/upload_photo");

        try {
            FileBody bin = new FileBody(new File("/home/ubuntu/cd.png"));
            StringBody id = new StringBody("3");
            MultipartEntity reqEntity = new MultipartEntity();
            reqEntity.addPart("upload_image", bin);
            reqEntity.addPart("id", id);
            reqEntity.addPart("image_title", new StringBody("CoolPic"));

            httppost.setEntity(reqEntity);
            System.out.println("Requesting : " + httppost.getRequestLine());
            ResponseHandler<String> responseHandler = new BasicResponseHandler();
            String responseBody = httpclient.execute(httppost, responseHandler);
            System.out.println("responseBody : " + responseBody);

        } catch (ClientProtocolException e) {

        } finally {
            httpclient.getConnectionManager().shutdown();
        }
    }

}

it requires below files to upload.

libraries are httpclient-4.1.2.jar, httpcore-4.1.2.jar, httpmime-4.1.2.jar, httpclient-cache-4.1.2.jar, commons-codec.jar and commons-logging-1.1.1.jar to be in classpath.

Java Man
  • 1,854
  • 3
  • 21
  • 43
4

You can also use REST Assured which builds on HTTP Client. It's very simple:

given().multiPart(new File("/somedir/file.bin")).when().post("/fileUpload");
Johan
  • 37,479
  • 32
  • 149
  • 237
  • It will assume a control name called "file". If you have a different control name then you need to specify it: `multiPart("controlName", new File("/somedir/file.bin"))` , see https://github.com/rest-assured/rest-assured/wiki/Usage#multi-part-form-data – asmaier Dec 08 '17 at 10:23
  • REST Assured has great API and supports many features. Working with it is a pleasure. But to be fair it's worth mentioning that due to some warm up procedures you may encounter decreased performance on the first call. You may find more info on the internet i.e. here https://sqa.stackexchange.com/questions/39532/rest-assured-vs-apache-httpclient-performance – user1053510 Feb 19 '20 at 07:42
  • REST Assured is a brilliant library, but it's designed for Web API testing and I don't think it's the right tool to make HTTP calls in production code, even though of course it make use of the same underlying libraries. – Ranil Wijeyratne May 19 '20 at 06:59
2

httpcomponents-client-4.0.1 worked for me. However, I had to add the external jar apache-mime4j-0.6.jar (org.apache.james.mime4j) otherwise reqEntity.addPart("bin", bin); would not compile. Now it's working like charm.

Ankur Mahajan
  • 3,396
  • 3
  • 31
  • 42
Bob Yoplait
  • 2,421
  • 1
  • 23
  • 35
2

I found this sample in Apache's Quickstart Guide. It's for version 4.5:

/**
 * Example how to use multipart/form encoded POST request.
 */
public class ClientMultipartFormPost {

    public static void main(String[] args) throws Exception {
        if (args.length != 1)  {
            System.out.println("File path not given");
            System.exit(1);
        }
        CloseableHttpClient httpclient = HttpClients.createDefault();
        try {
            HttpPost httppost = new HttpPost("http://localhost:8080" +
                    "/servlets-examples/servlet/RequestInfoExample");

            FileBody bin = new FileBody(new File(args[0]));
            StringBody comment = new StringBody("A binary file of some kind", ContentType.TEXT_PLAIN);

            HttpEntity reqEntity = MultipartEntityBuilder.create()
                    .addPart("bin", bin)
                    .addPart("comment", comment)
                    .build();


            httppost.setEntity(reqEntity);

            System.out.println("executing request " + httppost.getRequestLine());
            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    System.out.println("Response content length: " + resEntity.getContentLength());
                }
                EntityUtils.consume(resEntity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
}
Amber
  • 2,413
  • 1
  • 15
  • 20
2

You will happy!

   <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
   </dependency>
   <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.3.1</version>
   </dependency>

import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.ByteArrayBody;


byte[] byteArr1 = multipartFile1.getBytes();
byte[] byteArr2 = multipartFile2.getBytes();

HttpEntity reqEntity = MultipartEntityBuilder.create().setCharset(Charset.forName("UTF-8"))
                .addPart("image1", new ByteArrayBody(byteArr1, req.getMultipartFile1().getOriginalFilename()))
                .addPart("image2", new ByteArrayBody(byteArr2, req.getMultipartFile2().getOriginalFilename()))
                .build();
anson
  • 1,436
  • 14
  • 16
0

We have a pure java implementation of multipart-form submit without using any external dependencies or libraries outside jdk. Refer https://github.com/atulsm/https-multipart-purejava/blob/master/src/main/java/com/atul/MultipartPure.java

private static String body = "{\"key1\":\"val1\", \"key2\":\"val2\"}";
private static String subdata1 = "@@ -2,3 +2,4 @@\r\n";
private static String subdata2 = "<data>subdata2</data>";

public static void main(String[] args) throws Exception{        
    String url = "https://" + ip + ":" + port + "/dataupload";
    String token = "Basic "+ Base64.getEncoder().encodeToString((userName+":"+password).getBytes());

    MultipartBuilder multipart = new MultipartBuilder(url,token);       
    multipart.addFormField("entity", "main", "application/json",body);
    multipart.addFormField("attachment", "subdata1", "application/octet-stream",subdata1);
    multipart.addFormField("attachment", "subdata2", "application/octet-stream",subdata2);        
    List<String> response = multipart.finish();         
    for (String line : response) {
        System.out.println(line);
    }
}
Atul Soman
  • 4,612
  • 4
  • 30
  • 45
0

My code post multipartFile to server.

  public static HttpResponse doPost(
    String host,
    String path,
    String method,
    MultipartFile multipartFile
  ) throws IOException
  {

    HttpClient httpClient = wrapClient(host);
    HttpPost httpPost = new HttpPost(buildUrl(host, path));

    if (multipartFile != null) {

      HttpEntity httpEntity;

      ContentBody contentBody;
      contentBody = new ByteArrayBody(multipartFile.getBytes(), multipartFile.getOriginalFilename());
      httpEntity = MultipartEntityBuilder.create()
                                         .addPart("nameOfMultipartFile", contentBody)
                                         .build();

      httpPost.setEntity(httpEntity);

    }
    return httpClient.execute(httpPost);
  }
0

My code for sending files to server using post in multipart. Make use of multivalue map while making request for sending form data

  LinkedMultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
        map.add("FILE", new FileSystemResource(file));
        map.add("APPLICATION_ID", Number);

   httpService.post( map,headers);

At receiver end use

@RequestMapping(value = "fileUpload", method = RequestMethod.POST)
    public ApiResponse AreaCsv(@RequestParam("FILE") MultipartFile file,@RequestHeader("clientId") ){
//code
}
shubham kumar
  • 271
  • 2
  • 6
0

Using HttpRequestFactory to jira xray's /rest/raven/1.0/import/execution/cucumber/multipart :

Map<String, Object> params = new HashMap<>();
            params.put( "info", "zigouzi" );
            params.put(  "result", "baalo"  );
            HttpContent content = new UrlEncodedContent(params);

            OAuthParameters oAuthParameters = jiraOAuthFactory.getParametersForRequest(ACCESS_TOKEN, CONSUMER_KEY, PRIVATE_KEY);
            HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory(oAuthParameters);
            HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(url),   content);
            request.getHeaders().setAccept("application/json");
            String boundary = Long.toHexString(System.currentTimeMillis());
            request.getHeaders().setContentType("multipart/form-data; boundary="+boundary);
            request.getHeaders().setContentEncoding("application/json");
            HttpResponse response = null ;
            try
            {
                response = request.execute();
                Scanner s = new Scanner(response.getContent()).useDelimiter("\\A");
                result = s.hasNext() ? s.next() : "";
            }
            catch (Exception e)
            {
                 
            }

did the trick.