日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

服务器write后客户端响应,客户端解析服务器响应的multipart/form-data数据

發(fā)布時間:2025/3/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 服务器write后客户端响应,客户端解析服务器响应的multipart/form-data数据 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

multipart/form-data,多部件請求體。這個請求體比較特殊,它可以拆分為多個部件,每個部件都有自己的header和body,最常用的地方就是:客戶端文件上傳,因為有多個部件,在上傳文件的時候,還可以在body中添加其他的數(shù)據(jù)。json,form。。。

一般來說,都是客戶端發(fā)起multipart/form-data請求 ,服務器進行解析。而且這種東西的編碼解碼工作一般都是由底層的容器/框架完成。開發(fā)根本不必關心。但是我最近遇到了一個需求:

服務器響應multipart/form-data(包含了一個二進制文件和其他的文本數(shù)據(jù)),客戶端來解析

意味著,需要自己完成2個東西

在服務端完成multipart/form-data的數(shù)據(jù)編碼,并且響應給客戶端

在客戶端獲取到響應后,進行數(shù)據(jù)的解碼

multipart/form-data的請求體,看起來像這樣(省略了部分 header)

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

復制代碼

服務端的編碼

使用 org.apache.httpcomponents 庫進行編碼

org.apache.httpcomponents

httpmime

4.5.12

復制代碼

Controller

通過 MultipartEntityBuilder, 添加多個部件,每個部件有自己的名字,類型。構建出一個 HttpEntity對象。可以從這個對象中獲取到編碼后的IO流以及ContentType,直接響應給 客戶端就完事兒,比較簡單。

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()

// 表單 => (部件名稱,數(shù)據(jù),類型),要注意uri編碼

.addPart("name", new StringBody(UriUtils.encode("SpringBoot中文社區(qū)", StandardCharsets.UTF_8), ContentType.APPLICATION_FORM_URLENCODED))

// JSON => (部件名稱,JSON,類型)

.addPart("info", new StringBody("{\"site\": \"https://springboot.io\", \"year\": 2019}", ContentType.APPLICATION_JSON))

// 文件 => ( 部件名稱,文件,類型,文件名稱)

.addBinaryBody("logo", new File("D:\\logo.png"), ContentType.IMAGE_PNG, "logo.png")

.build();

// 設置ContentType

response.setContentType(httpEntity.getContentType().getValue());

// 響應客戶端

httpEntity.writeTo(response.getOutputStream());

}

}

復制代碼

客戶端的解碼

使用commons-fileupload 庫進行解碼

commons-fileupload

commons-fileupload

1.4

復制代碼

MultipartTest

看這個代碼,會覺得似曾相識。不錯,在Servlet3.0以前,HttpServletRequest還沒有getPart方法的時候 ,大家都是通過 commons-fileupload來從multipart/form-data請求中解析出數(shù)據(jù)的。

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;

/**

* 自己定義一個RequestContext的實現(xiàn)

*/

class SimpleRequestContext implements RequestContext{

private final Charset charset;// 編碼

private final MediaType contentType;// contentType

private final InputStream content;// 數(shù)據(jù)

public SimpleRequestContext(Charset charset, MediaType contentType, InputStream content){

this.charset = charset;

this.contentType = contentType;

this.content = content;

}

@Override

public String getCharacterEncoding(){

return this.charset.displayName();

}

@Override

public String getContentType(){

return this.contentType.toString();

}

@Override

public int getContentLength(){

try {

return this.content.available();

} catch (IOException e) {

}

return 0;

}

@Override

public InputStream getInputStream() throws IOException{

return this.content;

}

}

public class MultipartTest{

public static void main(String[] args) throws IOException, FileUploadException{

// 獲取服務器響應的IO流

RestTemplate restTemplate = new RestTemplate();

ResponseEntity responseEntity = restTemplate.getForEntity("http://localhost:8081/test", Resource.class);

// 創(chuàng)建RequestContext對象

RequestContext requestContext = new SimpleRequestContext(StandardCharsets.UTF_8, responseEntity.getHeaders().getContentType(),

responseEntity.getBody().getInputStream());

// 解析器創(chuàng)建

FileUploadBase fileUploadBase = new PortletFileUpload();

FileItemFactory fileItemFactory = new DiskFileItemFactory();

fileUploadBase.setFileItemFactory(fileItemFactory);

fileUploadBase.setHeaderEncoding(StandardCharsets.UTF_8.displayName());

// 解析出所有的部件

List fileItems = fileUploadBase.parseRequest(requestContext);

for (FileItem fileItem : fileItems) {

// 請求頭

System.out.println("headers:==========================");

FileItemHeaders fileItemHeaders = fileItem.getHeaders();

Iterator headerNamesIterator = fileItemHeaders.getHeaderNames();

while (headerNamesIterator.hasNext()) { // 迭代name

String headerName = headerNamesIterator.next();

Iterator headerValueIterator = fileItemHeaders.getHeaders(headerName);

while (headerValueIterator.hasNext()) {// 迭代value

String headerValue = headerValueIterator.next();

System.out.println(headerName + ":" + headerValue);

}

}

// 請求體

System.out.println("body:==========================");

if(fileItem.isFormField()) { // 是普通表單項

byte[] data = fileItem.get();

System.out.println(new String(data, StandardCharsets.UTF_8));

} else {// 是文件表單項

String fileName = fileItem.getName();// 文件的原始名稱

InputStream inputStream = fileItem.getInputStream();// 文件的IO流

System.out.println("fileName=" + fileName + ", size=" + inputStream.available());

}

System.out.println();

}

}

}

復制代碼

完整的日志輸出

17:18:55.384 [main] DEBUG org.springframework.web.client.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 [main] DEBUG org.springframework.web.client.RestTemplate - Response 200 OK

17:18:56.461 [main] 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=2423

復制代碼

客戶端準確的解析出了服務器響應的 multipart/form-data 數(shù)據(jù)。

總結

以上是生活随笔為你收集整理的服务器write后客户端响应,客户端解析服务器响应的multipart/form-data数据的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。