Comment puis-je effectuer une requête POST multipart / form-data à l'aide de Java?

96

À l'époque de la version 3.x d'Apache Commons HttpClient, il était possible de faire une requête POST multipart / form-data ( un exemple de 2004 ). Malheureusement, cela n'est plus possible dans la version 4.0 de HttpClient .

Pour notre activité principale "HTTP", le multipart est quelque peu hors de portée. Nous serions ravis d'utiliser du code en plusieurs parties maintenu par un autre projet pour lequel il est dans la portée, mais je n'en connais aucun. Nous avons essayé de déplacer le code en plusieurs parties vers le codec commun il y a quelques années, mais je n'ai pas décollé là-bas. Oleg a récemment mentionné un autre projet qui a un code d'analyse en plusieurs parties et pourrait être intéressé par notre code de formatage en plusieurs parties. Je ne connais pas la situation actuelle à ce sujet. ( http://www.nabble.com/multipart-form-data-in-4.0-td14224819.html )

Quelqu'un a-t-il connaissance d'une bibliothèque Java qui me permet d'écrire un client HTTP capable de faire une requête POST multipart / form-data?

Contexte: je souhaite utiliser l' API distante de Zoho Writer .


la source

Réponses:

151

Nous utilisons HttpClient 4.x pour publier des fichiers en plusieurs parties.

MISE À JOUR : Depuis HttpClient 4.3 , certaines classes sont obsolètes. Voici le code avec la nouvelle 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();

Vous trouverez ci-dessous l'extrait de code d'origine avec l' API HttpClient 4.0 obsolète :

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();
Codeur ZZ
la source
62
Ah, les éléments en plusieurs parties ont été déplacés vers org.apache.httpcomponents-httpmime-4.0! Pourrait être mentionné quelque part: /
J'ai essayé votre code mis à jour qui fonctionne bien avec les petits fichiers mais ne fonctionne pas avec les gros fichiers. Pouvez-vous m'aider avec cette question
AabinGunz
Bonjour ZZ, j'ai apporté la modification ci-dessus à mon code, cependant, je suis confronté à un nouveau problème maintenant - mon point de terminaison REST n'accepte pas la demande. Il attend les paramètres suivants: ~ @ PathVariable final String id, @RequestParam ("image") image finale du MultipartFile, @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 ... Auparavant, la requête était acceptée. Mais maintenant, j'obtiens une erreur 500. Des idées pourquoi cela pourrait se produire?
Logan
J'ai édité la réponse pour que l'exemple de code ne défile plus horizontalement - le défilement m'a fait manquer un paramètre final important lorsque j'ai essayé de l'utiliser dans mon propre travail.
G.Sylvie Davies
Voici les dépendances Maven pour la réponse mise à jour <dependency> <groupId> org.apache.httpcomponents </groupId> <artifactId> httpclient </artifactId> <version> 4.3.6 </version> </dependency> <! - mvnrepository.com/artifact/org.apache.httpcomponents/httpmime -> <dependency> <groupId> org.apache.httpcomponents </groupId> <artifactId> httpmime </artifactId> <version> 4.3.6 </version> < / dependency>
Wazime
39

Ce sont les dépendances Maven que j'ai.

Code Java:

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);

Dépendances Maven dans 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
la source
1
vous aurez également besoin de httpcore, au moins en 4.2, pour la HttpEntityclasse
alalonde
19

Si la taille des JAR est importante (par exemple dans le cas de l'applet), on peut aussi utiliser directement httpmime avec java.net.HttpURLConnection au lieu de 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();
...

Dépendance dans pom.xml:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.2.4</version>
</dependency>
anre
la source
FileBody d'où ça vient? Existe-t-il un moyen (simple) de ne pas utiliser apace.httpcomponents?
Jr.
6

Utilisez ce code pour télécharger des images ou tout autre fichier sur le serveur en utilisant la publication en plusieurs parties.

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();
        }
    }

}

il faut télécharger les fichiers ci-dessous.

les bibliothèques sont httpclient-4.1.2.jar, httpcore-4.1.2.jar, httpmime-4.1.2.jar, httpclient-cache-4.1.2.jar, commons-codec.jaret commons-logging-1.1.1.jardoivent être dans classpath.

Homme de Java
la source
4

Vous pouvez également utiliser REST Assured qui s'appuie sur le client HTTP. C'est très simple:

given().multiPart(new File("/somedir/file.bin")).when().post("/fileUpload");
Johan
la source
Il prendra un nom de contrôle appelé "fichier". Si vous avez un nom de contrôle différent, vous devez le spécifier :,multiPart("controlName", new File("/somedir/file.bin")) voir github.com/rest-assured/rest-assured/wiki/…
asmaier
REST Assured possède une excellente API et prend en charge de nombreuses fonctionnalités. Travailler avec c'est un plaisir. Mais pour être honnête, il convient de mentionner qu'en raison de certaines procédures de préchauffage, vous pouvez rencontrer une diminution des performances lors du premier appel. Vous pouvez trouver plus d'informations sur Internet, c'est-à-dire ici sqa.stackexchange.com/questions/39532/…
user1053510 le
REST Assured est une bibliothèque géniale, mais elle est conçue pour les tests d'API Web et je ne pense pas que ce soit le bon outil pour effectuer des appels HTTP dans le code de production, même si bien sûr, il utilise les mêmes bibliothèques sous-jacentes.
Ranil Wijeyratne
3

Voici une solution qui ne nécessite aucune bibliothèque.

Cette routine transmet chaque fichier du répertoire d:/data/mpf10à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
la source
2

httpcomponents-client-4.0.1travaillé pour moi. Cependant, j'ai dû ajouter le jar externe apache-mime4j-0.6.jar ( org.apache.james.mime4j ) sinon reqEntity.addPart("bin", bin);je ne compilerais pas. Maintenant ça marche comme du charme.

Bob Yoplait
la source
2

J'ai trouvé cet exemple dans le guide de démarrage rapide d'Apache . C'est pour la 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();
        }
    }
}
ambre
la source
0

Nous avons une implémentation pure Java de la soumission de formulaires en plusieurs parties sans utiliser de dépendances externes ou de bibliothèques en dehors de jdk. Reportez-vous à 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
la source
0

Mon code poste multipartFile au serveur.

  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);
  }
Martin521Wang
la source