Jak wysłać żądanie POST typu multipart / form-data przy użyciu języka Java?

96

W czasach wersji 3.x Apache Commons HttpClient możliwe było wysyłanie żądania POST typu multipart / form-data ( przykład z 2004 roku ). Niestety nie jest to już możliwe w wersji 4.0 HttpClient .

W przypadku naszej podstawowej działalności „HTTP” tryb wieloczęściowy jest nieco poza zakresem. Chcielibyśmy użyć kodu wieloczęściowego obsługiwanego przez inny projekt, dla którego jest on objęty zakresem, ale nie znam żadnego. Kilka lat temu próbowaliśmy przenieść kod wieloczęściowy do wspólnego kodera-dekodera, ale tego nie zrobiłem. Oleg niedawno wspomniał o innym projekcie, który ma wieloczęściowy kod parsujący i może być zainteresowany naszym kodem wieloczęściowym. Nie znam aktualnego stanu tego. ( http://www.nabble.com/multipart-form-data-in-4.0-td14224819.html )

Czy ktoś zna bibliotekę Java, która pozwala mi napisać klienta HTTP, który może wysyłać żądania POST typu multipart / form-data?

Kontekst: Chcę używać zdalnego interfejsu API Zoho Writer .


źródło

Odpowiedzi:

152

Używamy HttpClient 4.x do tworzenia wieloczęściowych postów w plikach.

AKTUALIZACJA : Począwszy od HttpClient 4.3 , niektóre klasy zostały wycofane. Oto kod z nowym 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();

Poniżej znajduje się oryginalny fragment kodu z wycofanym interfejsem 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();
ZZ Coder
źródło
63
Ach, elementy wieloczęściowe zostały przeniesione do org.apache.httpcomponents-httpmime-4.0! Można by gdzieś wspomnieć: /
Wypróbowałem zaktualizowany kod, który działa dobrze z małymi plikami, ale nie działa z dużymi plikami. Czy możesz mi pomóc z tym pytaniem
AabinGunz
Cześć ZZ, dokonałem powyższej zmiany w moim kodzie, jednak teraz mam nowy problem - mój punkt końcowy REST nie akceptuje żądania. Oczekuje następujących parametrów: ~ @ 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 ... Wcześniej żądanie było przyjmowane. Ale teraz otrzymuję błąd 500. Jakieś pomysły, dlaczego tak się dzieje?
Logan
Zredagowałem odpowiedź, aby przykładowy kod nie był już przewijany w poziomie - przewijanie spowodowało, że przegapiłem ważny końcowy parametr, gdy próbowałem go użyć we własnej pracy.
G. Sylvie Davies
Oto zależności Mavena dla zaktualizowanej odpowiedzi <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

Oto zależności Mavena, które mam.

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

Zależności Mavena w 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
źródło
1
będziesz potrzebować httpcore również, przynajmniej w 4.2, na HttpEntityzajęcia
alalonde
19

Jeśli rozmiar plików JAR ma znaczenie (np. W przypadku apletu), można również bezpośrednio użyć httpmime z java.net.HttpURLConnection zamiast 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

Kod:

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

Zależność w pom.xml:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpmime</artifactId>
    <version>4.2.4</version>
</dependency>
anre
źródło
FileBody, skąd to się wzięło? Czy istnieje (łatwy) sposób, aby nie używać komponentów apace.http?
Jr.
6

Użyj tego kodu, aby przesłać obrazy lub inne pliki na serwer za pomocą postu w wielu częściach.

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

}

wymaga przesłania poniższych plików.

biblioteki są httpclient-4.1.2.jar, httpcore-4.1.2.jar, httpmime-4.1.2.jar, httpclient-cache-4.1.2.jar, commons-codec.jari commons-logging-1.1.1.jarmają być w ścieżce klas.

Java Man
źródło
4

Możesz także użyć REST Assured, który jest oparty na kliencie HTTP. To jest bardzo proste:

given().multiPart(new File("/somedir/file.bin")).when().post("/fileUpload");
Johan
źródło
Przyjmie nazwę kontrolną o nazwie „plik”. Jeśli masz inną nazwę kontrolną, musisz ją określić: multiPart("controlName", new File("/somedir/file.bin"))zobacz github.com/rest-assured/rest-assured/wiki/ ...
asmaier
REST Assured ma świetne API i obsługuje wiele funkcji. Praca z nim to przyjemność. Ale żeby być uczciwym, warto wspomnieć, że z powodu pewnych procedur rozgrzewki możesz napotkać zmniejszoną wydajność podczas pierwszego połączenia. Więcej informacji można znaleźć w Internecie, np. Tutaj sqa.stackexchange.com/questions/39532/ ...
user1053510
REST Assured to genialna biblioteka, ale została zaprojektowana do testowania Web API i nie sądzę, że jest to właściwe narzędzie do wykonywania wywołań HTTP w kodzie produkcyjnym, chociaż oczywiście korzysta z tych samych bazowych bibliotek.
Ranil Wijeyratne
3

Oto rozwiązanie, które nie wymaga żadnych bibliotek.

Ta procedura przesyła każdy plik w katalogu d:/data/mpf10dourlToConnect


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
źródło
2

httpcomponents-client-4.0.1pracował dla mnie. Jednak musiałem dodać zewnętrzny jar apache-mime4j-0.6.jar ( org.apache.james.mime4j ), w przeciwnym reqEntity.addPart("bin", bin);razie nie mógłbym się skompilować. Teraz działa jak urok.

Bob Yoplait
źródło
2

Znalazłem ten przykład w przewodniku szybkiego startu Apache . Dotyczy wersji 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();
        }
    }
}
Bursztyn
źródło
0

Mamy czystą implementację w Javie przesyłania wieloczęściowego formularza bez użycia jakichkolwiek zewnętrznych zależności lub bibliotek poza jdk. Skorzystaj z 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
źródło
0

Mój kod wysyła plik multipartFile do serwera.

  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
źródło