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>,且註解中未指定引數名時,該 Map 將填充每個給定引數名的 multipart 檔案。

使用 Servlet 的 multipart 解析時,您也可以宣告 jakarta.servlet.http.Part 而非 Spring 的 MultipartFile,作為方法引數或集合值型別。

您還可以將 multipart 內容作為資料繫結的一部分繫結到命令物件。例如,前面示例中的表單欄位和檔案可以是表單物件上的欄位,如下例所示

  • 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 服務場景中,multipart 請求也可以由非瀏覽器客戶端提交。以下示例展示了一個包含 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 轉換後訪問 multipart

  • 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。更多詳情,請參閱校驗章節。