跳到主要内容
版本:7.0.3

多部分内容(Multipart Content)

Hunyuan 7b 中英对照 Multipart Content

Multipart Data 中所解释的,ServerWebExchange 提供了对多部分内容的访问。在控制器中处理文件上传表单(例如,来自浏览器的表单)的最佳方式是通过数据绑定到 命令对象,如下例所示:

class MyForm {

private String name;

private FilePart file;

// ...

}

@Controller
public class FileUploadController {

@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
// ...
}

}

在RESTful服务场景中,非浏览器客户端也可以提交多部分请求。以下示例使用了文件和JSON:

POST /someUrl
Content-Type: multipart/mixed

--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit

{
"name": "value"
}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...

如以下示例所示,你可以使用@RequestPart来访问各个部分:

@PostMapping("/")
public String handle(@RequestPart("meta-data") Part metadata, 1
@RequestPart("file-data") FilePart file) { 2
// ...
}
  • 使用 @RequestPart 获取元数据。

  • 使用 @RequestPart 获取文件。

要反序列化原始部分内容(例如,转换为JSON——类似于@RequestBody),你可以声明一个具体的目标Object,而不是Part,如下例所示:

@PostMapping('/')
public String handle(@RequestPart("meta-data") MetaData metadata) { 1
// ...
}
  • 使用 @RequestPart 来获取元数据。

您可以将 @RequestPartjakarta.validation VALID 或 Spring 的 @Validated 注解结合使用,这样就会应用标准 Bean 验证(Standard Bean Validation)。验证失败会引发 WebExchangeBindException 异常,导致返回 400(BAD_REQUEST)状态码的响应。该异常中包含一个包含错误详细信息的 BindingResult,也可以在控制器方法中通过为参数声明异步包装器(async wrapper),然后使用与错误相关的操作符来处理这些错误:

@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") Mono<MetaData> metadata) {
// use one of the onError* operators...
}

如果由于其他参数具有@Constraint注解而需要应用方法验证,则会抛出HandlerMethodValidationException异常。请参阅验证一节。

要将所有多部分数据作为MultiValueMap访问,可以使用@RequestBody,如下例所示:

@PostMapping("/")
public String handle(@RequestBody Mono<MultiValueMap<String, Part>> parts) { 1
// ...
}
  • 使用 @RequestBody

PartEvent

要顺序地、以流式方式访问多部分数据,你可以使用@RequestBodyFlux<PartEvent>(在Kotlin中为Flow<PartEvent>)。多部分HTTP消息中的每一部分都会产生至少一个PartEvent,其中包含该部分的头部信息以及该部分内容的缓冲区。

  • 表单字段将产生一个单个的FormPartEvent,其中包含该字段的值。
  • 文件上传将产生一个或多个FilePartEvent对象,其中包含上传时使用的文件名。如果文件太大而需要分成多个缓冲区上传,第一个FilePartEvent之后会跟随后续的事件。

例如:

@PostMapping("/")
public void handle(@RequestBody Flux<PartEvent> allPartEvents) {

// The final PartEvent for a particular part will have isLast() set to true, and can be
// followed by additional events belonging to subsequent parts.
// This makes the isLast property suitable as a predicate for the Flux::windowUntil operator, to
// split events from all parts into windows that each belong to a single part.
allPartEvents.windowUntil(PartEvent::isLast)
// The Flux::switchOnFirst operator allows you to see whether you are handling
// a form field or file upload
.concatMap(p -> p.switchOnFirst((signal, partEvents) -> {
if (signal.hasValue()) {
PartEvent event = signal.get();
if (event instanceof FormPartEvent formEvent) {
String value = formEvent.value();
// Handling of the form field
}
else if (event instanceof FilePartEvent fileEvent) {
String filename = fileEvent.filename();

// The body contents must be completely consumed, relayed, or released to avoid memory leaks
Flux<DataBuffer> contents = partEvents.map(PartEvent::content);
// Handling of the file upload
}
else {
return Mono.error(new RuntimeException("Unexpected event: " + event));
}
}
else {
return partEvents; // either complete or error signal
}
return Mono.empty();
}));
}

接收到的多部分数据事件也可以通过使用WebClient传递给其他服务。请参阅Multipart Data