Multipart /form-data, multipart request body. This request body is special because it can be split into multiple parts, each with its own header and body. The most common place is: client file upload. Because there are multiple parts, you can also add additional data to the body while uploading the file. Json, form…
Typically, the client initiates a multipart/form-data request and the server parses it. And the encoding and decoding of such things is usually done by the underlying container/framework. Development doesn’t have to care. But I recently encountered a need:
Server response
multipart/form-data
(contains a binary file and other text data), the client to parse
That means you have to do two things by yourself
- Do it on the server side
multipart/form-data
Encode the data and respond to the client - After the client receives the response, the data is decoded
multipart/form-data
The request body looks like this (part of the header is omitted).
POST /foo HTTP/1.1
Content-Length: 68137
Content-Type: multipart/form-data; boundary=---------------------------974767299852498929531610575
---------------------------974767299852498929531610575
Content-Disposition: form-data; name="description"
some text
---------------------------974767299852498929531610575
Content-Disposition: form-data; name="myFile"; filename="foo.txt"
Content-Type: text/plain
(content of the uploaded file foo.txt)
---------------------------974767299852498929531610575
Copy the code
Server coding
useorg.apache.httpcomponents
Library encoding
<! -- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.12</version>
</dependency>
Copy the code
Controller
With MultipartEntityBuilder, add multiple parts, each with its own name, type. Build an HttpEntity object. You can retrieve the encoded IO stream and ContentType from this object, and simply respond to the client.
import java.io.File;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletResponse;
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.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriUtils;
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping
public void test (HttpServletResponse response) throws Exception {
HttpEntity httpEntity = MultipartEntityBuilder.create()
// Form => (part name, data, type), pay attention to URI encoding
.addPart("name".new StringBody(UriUtils.encode("SpringBoot Chinese Community", StandardCharsets.UTF_8), ContentType.APPLICATION_FORM_URLENCODED))
// JSON => (component name, JSON, type)
.addPart("info".new StringBody("{\"site\": \"https://springboot.io\", \"year\": 2019}", ContentType.APPLICATION_JSON))
// File => (Part name, file, type, file name)
.addBinaryBody("logo".new File("D:\\logo.png"), ContentType.IMAGE_PNG, "logo.png")
.build();
/ / set the ContentType
response.setContentType(httpEntity.getContentType().getValue());
// Respond to the clienthttpEntity.writeTo(response.getOutputStream()); }}Copy the code
Client decoding
usecommons-fileupload
Library decoding
<! -- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
Copy the code
MultipartTest
If you look at this code, it’s deja vu. True, prior to Servlet3.0, HttpServletRequest did not have a getPart method, so commons-fileupload was used to parse data from multipart/form-data requests.
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileItemHeaders;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.RequestContext;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.portlet.PortletFileUpload;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
/** * define your own implementation of RequestContext */
class SimpleRequestContext implements RequestContext {
private final Charset charset; / / code
private final MediaType contentType; // contentType
private final InputStream content; / / data
public SimpleRequestContext(Charset charset, MediaType contentType, InputStream content) {
this.charset = charset;
this.contentType = contentType;
this.content = content;
}
@Override
public String getCharacterEncoding(a) {
return this.charset.displayName();
}
@Override
public String getContentType(a) {
return this.contentType.toString();
}
@Override
public int getContentLength(a) {
try {
return this.content.available();
} catch (IOException e) {
}
return 0;
}
@Override
public InputStream getInputStream(a) throws IOException {
return this.content; }}public class MultipartTest {
public static void main(String[] args) throws IOException, FileUploadException {
// Get the IO stream for the server response
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<Resource> responseEntity = restTemplate.getForEntity("http://localhost:8081/test", Resource.class);
// Create a RequestContext
RequestContext requestContext = new SimpleRequestContext(StandardCharsets.UTF_8, responseEntity.getHeaders().getContentType(),
responseEntity.getBody().getInputStream());
// Create a parser
FileUploadBase fileUploadBase = new PortletFileUpload();
FileItemFactory fileItemFactory = new DiskFileItemFactory();
fileUploadBase.setFileItemFactory(fileItemFactory);
fileUploadBase.setHeaderEncoding(StandardCharsets.UTF_8.displayName());
// Parse out all the components
List<FileItem> fileItems = fileUploadBase.parseRequest(requestContext);
for (FileItem fileItem : fileItems) {
/ / request header
System.out.println("headers:==========================");
FileItemHeaders fileItemHeaders = fileItem.getHeaders();
Iterator<String> headerNamesIterator = fileItemHeaders.getHeaderNames();
while (headerNamesIterator.hasNext()) { / / iteration name
String headerName = headerNamesIterator.next();
Iterator<String> headerValueIterator = fileItemHeaders.getHeaders(headerName);
while (headerValueIterator.hasNext()) { / / iteration value
String headerValue = headerValueIterator.next();
System.out.println(headerName + ":"+ headerValue); }}/ / request body
System.out.println("body:==========================");
if(fileItem.isFormField()) { // is a normal form entry
byte[] data = fileItem.get();
System.out.println(new String(data, StandardCharsets.UTF_8));
} else { // is a file form entry
String fileName = fileItem.getName(); // The original name of the file
InputStream inputStream = fileItem.getInputStream(); // File IO stream
System.out.println("fileName=" + fileName + ", size="+ inputStream.available()); } System.out.println(); }}}Copy the code
Complete log output
17:18:55. 384. [the main] the DEBUG org. Springframework. Web. Client. The RestTemplate - HTTP GET http://localhost:8081/test 17:18:55. 449 [main] DEBUG org.springframework.web.client.RestTemplate - Accept=[application/json, application/*+json, * / *] 17:18:56. 426. [the main] the DEBUG org. Springframework. Web. Client. The RestTemplate - 200 OK Response 17:18:56, 461 [main] the DEBUG org.springframework.web.client.RestTemplate - Reading to [org.springframework.core.io.Resource] as "multipart/form-data; boundary=0W40KHiHJTyo5H_n1EIL68aM4tNRhPa-7Vp" headers:========================== content-disposition:form-data; name="name" content-type:application/x-www-form-urlencoded; charset=ISO-8859-1 content-transfer-encoding:8bit body:========================== SpringBoot%E4%B8%AD%E6%96%87%E7%A4%BE%E5%8C%BA headers:========================== content-disposition:form-data; name="info" content-type:application/json; charset=UTF-8 content-transfer-encoding:8bit body:========================== {"site": "https://springboot.io", "year": 2019} headers:========================== content-disposition:form-data; name="logo"; filename="logo.png" content-type:image/png content-transfer-encoding:binary body:========================== fileName=logo.png, size=2423Copy the code
The client correctly parses the multipart/form-data data of the server response.
Release: springboot. IO/topic / 255 / t…