多部分

在启用了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();
			// 将字节存储在某个地方
			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;
			// 将字节存储在某个地方
			return "redirect:uploadSuccess";
		}
		return "redirect:uploadFailure";
	}
}

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

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

使用Servlet多部分解析时,您还可以将jakarta.servlet.http.Part声明为方法参数或集合值类型,而不是Spring的MultipartFile

您还可以将多部分内容作为数据绑定的一部分绑定到命令对象。例如,前面示例中的表单字段和文件可以作为表单对象的字段,如下例所示:

  • 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();
			// 将字节存储在某个地方
			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;
			// 将字节存储在某个地方
			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
... 文件数据 ...

您可以使用@RequestParam作为String访问"meta-data"部分,但您可能希望将其从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 {
	// ...
}

您可以将@RequestPartjakarta.validation.Valid结合使用,或使用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。有关更多详细信息,请参阅验证部分。