HttpClient
是JDK11提供的一个全新HTTP客户端Api,超级好用。
Multipart 申请
HttpClient 并没有提供 Multipart 申请体的构建Api。然而能够应用apache的开源httpmime库来进行构建。
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime --><dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.5.13</version></dependency>
构建一个 MultipartBody
// 构建Multipart申请HttpEntity httpEntity = MultipartEntityBuilder.create() // 表单数据 .addPart("name", new StringBody(UriUtils.encode("SpringBoot中文社区", StandardCharsets.UTF_8), ContentType.APPLICATION_FORM_URLENCODED)) // JSON数据 .addPart("info", new StringBody("{\"site\": \"https://springboot.io\", \"now\": 2021}", ContentType.APPLICATION_JSON)) // 文件数据 .addBinaryBody("file", file, ContentType.APPLICATION_OCTET_STREAM, "eclipse-jee-2019-12-R-win32-x86_64.zip") .build();ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream((int) httpEntity.getContentLength());// 把body写入到内存httpEntity.writeTo(byteArrayOutputStream);
Multipart 申请能够一次性post多个子body,通常用来上传本地磁盘上的文件。所以这种申请体可能会异样宏大。甚至内存不能残缺的存入整个申请体。那么这个时候有2种方法能够解决。
- 先把构建好的Body数据写入到磁盘,再通过IO磁盘数据,提交给服务器
- 应用管道流,在读取磁盘数据进行body构建的时候,间接通过管道提交到近程服务器
管道流
管道流,顾名思义,能够往一边写,从另一边读。
// 创立读取流PipedInputStream pipedInputStream = new PipedInputStream();// 创立写入流PipedOutputStream pipedOutputStream = new PipedOutputStream();// 连贯写入和读取流pipedInputStream.connect(pipedOutputStream);
通过往pipedOutputStream
写入数据,就能够在pipedInputStream
读取。
不能应用单线程既读又写,因为读写都是阻塞办法。任何一方阻塞住了了,另一方就会始终处于期待状态,导致死锁
残缺Demo
客户端
package io.springcloud.test;import java.io.IOException;import java.io.InputStream;import java.io.PipedInputStream;import java.io.PipedOutputStream;import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpRequest.BodyPublishers;import java.net.http.HttpResponse;import java.net.http.HttpResponse.BodyHandlers;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.nio.file.Paths;import org.apache.http.HttpEntity;import org.apache.http.entity.ContentType;import org.apache.http.entity.mime.MultipartEntityBuilder;import org.apache.http.entity.mime.content.StringBody;import org.springframework.web.util.UriUtils;public class MainTest { public static void main(String[] args) throws Exception { // 管道流 PipedInputStream pipedInputStream = new PipedInputStream(); PipedOutputStream pipedOutputStream = new PipedOutputStream(); pipedInputStream.connect(pipedOutputStream); // 本地文件 InputStream file = Files.newInputStream(Paths.get("D:\\eclipse-jee-2019-12-R-win32-x86_64.zip")); // 构建Multipart申请 HttpEntity httpEntity = MultipartEntityBuilder.create() // 表单数据 .addPart("name", new StringBody(UriUtils.encode("SpringBoot中文社区", StandardCharsets.UTF_8), ContentType.APPLICATION_FORM_URLENCODED)) // JSON数据 .addPart("info", new StringBody("{\"site\": \"https://springboot.io\", \"now\": 2021}", ContentType.APPLICATION_JSON)) // 文件数据 .addBinaryBody("file", file, ContentType.APPLICATION_OCTET_STREAM, "eclipse-jee-2019-12-R-win32-x86_64.zip") .build(); // 异步写入数据到管道流 new Thread(() -> { try (file; pipedOutputStream){ httpEntity.writeTo(pipedOutputStream); } catch (IOException e) { e.printStackTrace(); } }).start(); HttpClient httpClient = HttpClient.newHttpClient(); try (pipedInputStream){ // 创立申请和申请体 HttpRequest request = HttpRequest .newBuilder(new URI("http://localhost/upload")) // 设置ContentType .header("Content-Type", httpEntity.getContentType().getValue()) .header("Accept", "text/plain") // 从管道流读取数据,提交给服务器 .POST(BodyPublishers.ofInputStream(() -> pipedInputStream)) .build(); // 执行申请,获取响应 HttpResponse<String> responseBody = httpClient.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); System.out.println(responseBody.body()); } }}
服务端
package io.springcloud.web.controller;import java.nio.charset.StandardCharsets;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestPart;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;import org.springframework.web.util.UriUtils;import com.google.gson.JsonObject;@RestController@RequestMapping("/upload")public class UploadController { private static final Logger LOGGER = LoggerFactory.getLogger(UploadController.class); @PostMapping public Object upload (@RequestPart("file") MultipartFile file, @RequestPart("info") JsonObject info, @RequestPart("name") String name) { LOGGER.info("file: name={}, size={}", file.getOriginalFilename(), file.getSize()); LOGGER.info("info: {}", info.toString()); LOGGER.info("name: {}", UriUtils.decode(name, StandardCharsets.UTF_8)); return ResponseEntity.ok("success"); }}
启动服务端后,执行客户端申请,服务端日志输入
2021-09-24 13:38:15.067 INFO 2660 --- [ XNIO-1 task-1] i.s.web.controller.UploadController : file: name=eclipse-jee-2019-12-R-win32-x86_64.zip, size=3696531472021-09-24 13:38:15.067 INFO 2660 --- [ XNIO-1 task-1] i.s.web.controller.UploadController : info: {"site":"https://springboot.io","now":2021}2021-09-24 13:38:15.067 INFO 2660 --- [ XNIO-1 task-1] i.s.web.controller.UploadController : name: SpringBoot中文社区
首发:https://springboot.io/t/topic...