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 responsemultipart/form-data(contains a binary file and other text data), the client to parse

That means you have to do two things by yourself

  1. Do it on the server sidemultipart/form-dataEncode the data and respond to the client
  2. After the client receives the response, the data is decoded

multipart/form-dataThe 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.httpcomponentsLibrary 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-fileuploadLibrary 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…