如何使用Java 11 HTTP客户端为POST请求定义多个参数

15

我有一段代码,用于向特定端点发出POST请求。这段代码使用了Apache的HttpClient,我想开始使用来自Java(JDK11)的原生HttpClient。但我不知道如何指定请求的参数。

以下是我使用Apache Httpclient的代码:

var path = Path.of("file.txt");
var entity = MultipartEntityBuilder.create()
            .addPart("file", new FileBody(path.toFile()))
            .addTextBody("token", "<any-token>")
            .build();

而使用 HttpClient 的代码:

var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
                         .uri(URI.create("https://myendpoint.com/"))
                         .POST( /* How can I set the parameters here? */ );

我该如何设置filetoken参数?


Java 11 HttpClient似乎不支持文件上传。此外,它似乎要求您已经在某个地方设置了所有POST参数(与GET参数完全相同),而不是为它们建立一个构建器。 - Powerlord
1
话虽如此,但已经有其他人编写了自定义BodyPublisher来执行文件上传。 - Powerlord
我不是很确定,但或许可以试试这个:HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://myendpoint.com/")) .header("Content-Type", "multipart/form-data") .POST(HttpRequest.BodyPublishers.ofFile(Path.of("file.txt"))) .POST(HttpRequest.BodyPublishers.ofString("token")) .build(); - Naman
2个回答

16

不幸的是,Java 11 HTTP客户端没有提供对多部分(multipart)请求体的便捷支持。但我们可以在其基础上构建自定义实现:

Map<Object, Object> data = new LinkedHashMap<>();
data.put("token", "some-token-value");
data.put("file", File.createTempFile("temp", "txt").toPath());

// add extra parameters if needed

// Random 256 length string is used as multipart boundary
String boundary = new BigInteger(256, new Random()).toString();

HttpRequest.newBuilder()
              .uri(URI.create("http://example.com"))
              .header("Content-Type", "multipart/form-data;boundary=" + boundary)
              .POST(ofMimeMultipartData(data, boundary))
              .build();

public HttpRequest.BodyPublisher ofMimeMultipartData(Map<Object, Object> data,
                                                     String boundary) throws IOException {
        // Result request body
        List<byte[]> byteArrays = new ArrayList<>();

        // Separator with boundary
        byte[] separator = ("--" + boundary + "\r\nContent-Disposition: form-data; name=").getBytes(StandardCharsets.UTF_8);

        // Iterating over data parts
        for (Map.Entry<Object, Object> entry : data.entrySet()) {

            // Opening boundary
            byteArrays.add(separator);

            // If value is type of Path (file) append content type with file name and file binaries, otherwise simply append key=value
            if (entry.getValue() instanceof Path) {
                var path = (Path) entry.getValue();
                String mimeType = Files.probeContentType(path);
                byteArrays.add(("\"" + entry.getKey() + "\"; filename=\"" + path.getFileName()
                        + "\"\r\nContent-Type: " + mimeType + "\r\n\r\n").getBytes(StandardCharsets.UTF_8));
                byteArrays.add(Files.readAllBytes(path));
                byteArrays.add("\r\n".getBytes(StandardCharsets.UTF_8));
            } else {
                byteArrays.add(("\"" + entry.getKey() + "\"\r\n\r\n" + entry.getValue() + "\r\n")
                        .getBytes(StandardCharsets.UTF_8));
            }
        }

        // Closing boundary
        byteArrays.add(("--" + boundary + "--").getBytes(StandardCharsets.UTF_8));

        // Serializing as byte array
        return HttpRequest.BodyPublishers.ofByteArrays(byteArrays);
    }

这里是在Github上的可运行示例(您需要更改VirusTotal API密钥)


2
只是想问一下,BodyPublishers.ofMimeMultipartData(data) 在 Java-11 中是否存在?至少我在 Java-12 中找不到它。 - Naman
@Naman 我这里也一样,刚在这里和这里找到了ofMimeMultipartData的参考。看起来是自定义实现,不是JDK的一部分。 - Renan Gomes
@Renan 确实,所以关键在于主要依赖于 BodyPublishers.ofByteArray(s) - Naman
抱歉,大家,我错过了这一部分。是的,这是一个自定义实现。@Renan提供的链接是正确的。已更新答案。 - Mikhail Kholodkov

0

HttpClient 没有提供任何高级 API 来组合或格式化 POST 请求中的数据。您可以手动组合和格式化您的 POST 数据,然后使用 BodyPublishers.ofString()BodyPublishers.ofInputStream()BodyPublishers.ofByteArrays() 等其中之一来发送它,或编写自己的 BodyPublisher 实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接