Multipart

在启用 MultipartResolver 后,将会解析 multipart/form-data 的 POST 请求的内容,并且可以作为常规请求参数访问。以下示例访问了一个常规表单字段和一个上传文件:

  • Java

  • Kotlin

@Controller
public class FileUploadController {

	@PostMapping("/form")
	public String handleFormUpload(@RequestParam("name") String name,
			@RequestParam("file") MultipartFile file) {

		if (!file.isEmpty()) {
			byte[] bytes = file.getBytes();
			// store the bytes somewhere
			return "redirect:uploadSuccess";
		}
		return "redirect:uploadFailure";
	}
}
@Controller
class FileUploadController {

	@PostMapping("/form")
	fun handleFormUpload(@RequestParam("name") name: String,
						@RequestParam("file") file: MultipartFile): String {

		if (!file.isEmpty) {
			val bytes = file.bytes
			// store the bytes somewhere
			return "redirect:uploadSuccess"
		}
		return "redirect:uploadFailure"
	}
}

将参数类型声明为 List<MultipartFile> 允许解析同一参数名称的多个文件。

@RequestParam 注解作为 Map<String, MultipartFile>MultiValueMap<String, MultipartFile> 声明,而注解中未指定参数名时,则该映射将填充给定参数名的每个多部分文件。

在 Servlet 多部分解析中,也可以声明 jakarta.servlet.http.Part 而不是 Spring 的 MultipartFile,作为方法参数或集合值类型。

您还可以使用multipart 内容来对 command object 进行数据绑定的一部分。例如,前面的示例中的表单字段和文件可以是表单对象上的字段,如下面的示例所示:

  • Java

  • Kotlin

class MyForm {

	private String name;

	private MultipartFile file;

	// ...
}

@Controller
public class FileUploadController {

	@PostMapping("/form")
	public String handleFormUpload(MyForm form, BindingResult errors) {
		if (!form.getFile().isEmpty()) {
			byte[] bytes = form.getFile().getBytes();
			// store the bytes somewhere
			return "redirect:uploadSuccess";
		}
		return "redirect:uploadFailure";
	}
}
class MyForm(val name: String, val file: MultipartFile, ...)

@Controller
class FileUploadController {

	@PostMapping("/form")
	fun handleFormUpload(form: MyForm, errors: BindingResult): String {
		if (!form.file.isEmpty) {
			val bytes = form.file.bytes
			// store the bytes somewhere
			return "redirect:uploadSuccess"
		}
		return "redirect:uploadFailure"
	}
}

多部分请求也可以在 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 …​

您可以使用 @RequestParam 访问“元数据”部分,作为 String,但您可能希望对其从 JSON 中进行反序列化(类似于 @RequestBody)。使用 @RequestPart 注释来访问多部分,方法是使用 HttpMessageConverter 将其转换后访问:

  • Java

  • Kotlin

@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata,
		@RequestPart("file-data") MultipartFile file) {
	// ...
}
@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData,
		@RequestPart("file-data") file: MultipartFile): String {
	// ...
}

你可以在与 jakarta.validation.Valid 结合使用 @RequestPart 或使用 Spring 的 @Validated 注解,这两个都会导致标准 Bean 验证被应用。默认情况下,验证错误会导致 MethodArgumentNotValidException,它会被转换为 400 (BAD_REQUEST) 响应。另外,你可以在控制器中通过 ErrorsBindingResult 参数本地处理验证错误,如下例所示:

  • Java

  • Kotlin

@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") MetaData metadata, Errors errors) {
	// ...
}
@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData, errors: Errors): String {
	// ...
}

如果方法验证适用,因为其他参数具有 @Constraint 注释,则会改为引发 HandlerMethodValidationException。有关更多详细信息,请参阅 Validation 部分。