HttpClient is a new HTTP client Api provided by JDK11.
Multipart request
HttpClient does not provide a build Api for the Multipart request body. However, you can use Apache’s open source HttpMIME library to build.
<! -- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
Copy the code
Build a MultipartBody
// Build the Multipart request
HttpEntity httpEntity = MultipartEntityBuilder.create()
// Form data
.addPart("name".new StringBody(UriUtils.encode("SpringBoot Chinese Community", StandardCharsets.UTF_8), ContentType.APPLICATION_FORM_URLENCODED))
/ / the JSON data
.addPart("info".new StringBody("{\"site\": \"https://springboot.io\", \"now\": 2021}", ContentType.APPLICATION_JSON))
// File data
.addBinaryBody("file", file, ContentType.APPLICATION_OCTET_STREAM, "eclipse-jee-2019-12-R-win32-x86_64.zip")
.build();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream((int) httpEntity.getContentLength());
// Write the body to memory
httpEntity.writeTo(byteArrayOutputStream);
Copy the code
A Multipart request can post multiple sub-bodies at once, usually to upload files on a local disk. So the request body can be very large. Even memory does not fit the entire request body. So there are two ways to solve this problem.
- The Body data is first written to disk and then submitted to the server via IO disk data
- Using a pipe flow, you pipe directly to a remote server while reading disk data for a body build
The pipe flow
Pipe flows, as the name implies, can be written to one side and read from the other.
// Create a read stream
PipedInputStream pipedInputStream = new PipedInputStream();
// Create a write stream
PipedOutputStream pipedOutputStream = new PipedOutputStream();
// Connect the write and read streams
pipedInputStream.connect(pipedOutputStream);
Copy the code
By writing data to pipedOutputStream, you can read from pipedInputStream.
You cannot use a single thread to read and write at the same time, because reading and writing are blocking methods. If either party blocks, the other party will remain in a waiting state, resulting in a deadlock
Complete Demo
The client
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 {
/ / pipe flow
PipedInputStream pipedInputStream = new PipedInputStream();
PipedOutputStream pipedOutputStream = new PipedOutputStream();
pipedInputStream.connect(pipedOutputStream);
// Local file
InputStream file = Files.newInputStream(Paths.get("D:\\eclipse-jee-2019-12-R-win32-x86_64.zip"));
// Build the Multipart request
HttpEntity httpEntity = MultipartEntityBuilder.create()
// Form data
.addPart("name".new StringBody(UriUtils.encode("SpringBoot Chinese Community", StandardCharsets.UTF_8), ContentType.APPLICATION_FORM_URLENCODED))
/ / the JSON data
.addPart("info".new StringBody("{\"site\": \"https://springboot.io\", \"now\": 2021}", ContentType.APPLICATION_JSON))
// File data
.addBinaryBody("file", file, ContentType.APPLICATION_OCTET_STREAM, "eclipse-jee-2019-12-R-win32-x86_64.zip")
.build();
// Write data asynchronously to the pipe stream
new Thread(() -> {
try (file; pipedOutputStream){
httpEntity.writeTo(pipedOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
HttpClient httpClient = HttpClient.newHttpClient();
try (pipedInputStream){
// Create the request and request body
HttpRequest request = HttpRequest
.newBuilder(new URI("http://localhost/upload"))
/ / set the ContentType
.header("Content-Type", httpEntity.getContentType().getValue())
.header("Accept"."text/plain")
// Read data from the pipeline stream and submit it to the server
.POST(BodyPublishers.ofInputStream(() -> pipedInputStream))
.build();
// Execute the request and get the responseHttpResponse<String> responseBody = httpClient.send(request, BodyHandlers.ofString(StandardCharsets.UTF_8)); System.out.println(responseBody.body()); }}}Copy the code
The service side
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"); }}Copy the code
After the server is started, the client requests are executed and the server logs are generated
The 13:38:15 2021-09-24. 2660-067 the INFO [task XNIO - 1-1] I.S.W eb. Controller. UploadController: file: name=eclipse-jee-2019-12-R-win32-x86_64.zip, Size = 369653147 2021-09-24 13:38:15. 2660-067 the INFO] [task XNIO - 1-1 I.S.W eb. Controller. UploadController: the INFO: {"site":"https://springboot.io","now":2021} 2021-09-24 13:38:15.067 INFO 2660 -- [xnio-1 task-1] I.S.W eb. Controller. UploadController: name: SpringBoot Chinese communityCopy the code
Starting: springboot. IO/topic / 418 / t…