You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache HttpComponents 5:使用HttpAsyncClient发送FormBodyPart定义的多部分表单数据的实现问题

Apache HttpComponents 5:使用HttpAsyncClient发送FormBodyPart定义的多部分表单数据的实现问题

这个问题我之前也碰到过,HttpComponents 5把同步和异步的实体构建逻辑做了明确区分,确实容易在这个点上卡壳。你现在的核心问题有两个:一是误用了同步的MultipartEntityBuilder,异步环境需要用专门的异步构建器来生成AsyncEntityProducer;二是调用execute方法时参数不对,异步客户端需要的是请求生产者而非直接的实体生产者。

下面是修正后的完整可运行代码,我会标注关键修改点:

import org.apache.hc.client5.http.async.methods.HttpAsyncMethods;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.mime.AsyncMultipartEntityBuilder;
import org.apache.hc.client5.http.entity.mime.FormBodyPart;
import org.apache.hc.client5.http.entity.mime.FormBodyPartBuilder;
import org.apache.hc.client5.http.entity.mime.content.FileBody;
import org.apache.hc.client5.http.entity.mime.content.StringBody;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.nio.DefaultHttpAsyncClientConfig;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.nio.AsyncEntityProducer;
import org.apache.hc.core5.http.nio.support.AsyncRequestProducer;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.util.Timeout;

import java.io.File;
import java.util.concurrent.Future;

public class AsyncMultipartUploadExample {
    public static void main(String[] args) throws Exception {
        // 1. 原有的FormBodyPart可以完全复用,不需要修改
        FormBodyPart textPart = FormBodyPartBuilder.create("metadata")
                .setBody(new StringBody(
                        "{\"author\":\"John Doe\"}", ContentType.APPLICATION_JSON))
                .build();

        FormBodyPart filePart = FormBodyPartBuilder.create("document")
                .setBody(new FileBody(
                        new File("report.pdf"), ContentType.APPLICATION_PDF, "Q3_Report.pdf"))
                .addField("X-File-Description", "Quarterly Report")
                .build();

        // 2. 关键修改1:使用异步版的Multipart构建器,直接生成AsyncEntityProducer
        AsyncEntityProducer entityProducer = AsyncMultipartEntityBuilder.create()
                .setBoundary("----MyBoundary123")
                .addPart(textPart)
                .addPart(filePart)
                .build();

        // 3. 关键修改2:构建POST请求,并绑定实体生产者
        // 替换成你的目标API地址
        SimpleHttpRequest request = SimpleRequestBuilder.post("http://your-upload-api.com/upload")
                .setEntityProducer(entityProducer)
                .build();

        // 把请求转换成异步请求生产者
        AsyncRequestProducer requestProducer = HttpAsyncMethods.create(request);

        SimpleResponseConsumer responseConsumer = new SimpleResponseConsumer();

        // 修正客户端初始化的语法错误(原代码少了build()调用和闭合括号)
        try (CloseableHttpAsyncClient client = HttpAsyncClients.custom()
                .setDefaultRequestConfig(RequestConfig.custom()
                        .setConnectTimeout(Timeout.ofSeconds(10))
                        .build())
                .setIOReactorConfig(DefaultHttpAsyncClientConfig.custom()
                        .setSoTimeout(Timeout.ofSeconds(30))
                        .build())
                .build()) {

            client.start();

            // 4. 关键修改3:传入请求生产者而非直接传实体生产者
            Future<SimpleHttpResponse> future = client.execute(
                    requestProducer,
                    responseConsumer,
                    new FutureCallback<SimpleHttpResponse>() {
                        @Override
                        public void completed(SimpleHttpResponse response) {
                            System.out.println("code: " + response.getCode());
                            System.out.println("response: " + response.getBodyText());
                        }

                        @Override
                        public void failed(Exception ex) {
                            System.err.println("failed: " + ex.getMessage());
                            ex.printStackTrace();
                        }

                        @Override
                        public void cancelled() {
                            System.out.println("cancel");
                        }
                    });

            // 等待请求完成(实际业务中如果不需要阻塞可以去掉这行,靠回调处理)
            future.get();

            // 优雅关闭客户端
            client.close(CloseMode.GRACEFUL);
        }
    }
}

核心修改点解释:

  1. 异步多部分实体构建:用AsyncMultipartEntityBuilder替代同步的MultipartEntityBuilder,它专门为异步场景设计,支持直接添加FormBodyPart,最终生成AsyncEntityProducer,完美匹配异步客户端的需求。
  2. 正确构建请求生产者:异步客户端的execute方法需要的是HttpAsyncRequestProducer(请求生产者),而非直接的实体生产者。我们通过SimpleRequestBuilder创建POST请求,绑定实体后用HttpAsyncMethods.create()转换成请求生产者。
  3. 修正语法错误:原代码中CloseableHttpAsyncClient的初始化缺少build()调用和闭合括号,这会导致编译失败,已经在代码中修复。

另外要注意:实际业务中如果不需要阻塞等待结果,可以去掉future.get(),让回调异步处理响应即可;如果需要自定义请求配置(比如连接超时),可以通过setDefaultRequestConfig设置。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 11:14:53