Spring MVC 通過 CommonsMultipartResolver 封裝 Apache Commons FileUpload,提供優雅的文件上傳 API。本文涵蓋解析器配置、表單要求、單/多文件上傳 Controller 代碼、文件下載三要素、以及 App 端上傳說明。
在 Spring 配置文件中定義 id 必須為 multipartResolver:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8"/>
<property name="maxUploadSize" value="10485760"/> <!-- 10MB -->
<property name="maxInMemorySize" value="1048576"/> <!-- 1MB 以下內存暫存 -->
</bean>
依賴 JAR(放入 WEB-INF/lib 或 Maven):
commons-fileupload-1.3.3.jarcommons-io-2.6.jar<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="file" /> <!-- name 必須與 Controller 參數一致 -->
<input type="submit" value="上傳"/>
</form>
enctype="multipart/form-data" 必須設置。name="file" 對應後端 MultipartFile file 或 CommonsMultipartFile file。@Controller
public class UploadController {
@RequestMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {
if (file.isEmpty()) return "error";
String originalName = file.getOriginalFilename();
String suffix = originalName.substring(originalName.lastIndexOf("."));
String newName = UUID.randomUUID().toString() + suffix;
String rootPath = request.getServletContext().getRealPath("/upload");
File destDir = new File(rootPath);
if (!destDir.exists()) destDir.mkdirs();
File destFile = new File(destDir, newName);
try {
file.transferTo(destFile); // Spring 內部使用 FileCopyUtils
} catch (IOException e) {
e.printStackTrace();
return "error";
}
return "success";
}
}
注意:@RequestParam("file") 必須加,否則綁定失敗報錯。
@RequestMapping("/multiUpload")
public String multiUpload(@RequestParam("files") MultipartFile[] files, HttpServletRequest request) {
for (MultipartFile file : files) {
if (!file.isEmpty()) {
// 同單文件處理邏輯
String newName = UUID.randomUUID() + file.getOriginalFilename().substring(...);
file.transferTo(new File(uploadDir, newName));
}
}
return "success";
}
前端:<input type="file" name="files" multiple/>
@RequestMapping("/download")
public void download(HttpServletResponse response, @RequestParam String fileName) throws IOException {
String realPath = request.getServletContext().getRealPath("/upload/" + fileName);
File file = new File(realPath);
if (!file.exists()) { response.sendError(404); return; }
// 1. 設置編碼
response.setContentType("application/octet-stream;charset=UTF-8");
// 2. Content-Disposition: attachment; filename=編碼後文件名
String encodedName = URLEncoder.encode(fileName, "UTF-8").replace("+", "%20");
response.setHeader("Content-Disposition", "attachment;filename=" + encodedName);
// 3. Content-Length
response.setHeader("Content-Length", String.valueOf(file.length()));
// 字節流輸出
try (InputStream in = new FileInputStream(file);
OutputStream out = response.getOutputStream()) {
byte[] buffer = new byte[1024 * 1024];
int len;
while ((len = in.read(buffer)) > 0) out.write(buffer, 0, len);
}
}
Android/iOS 同樣使用 HTTP multipart/form-data 格式,只需在請求頭設置 Content-Type: multipart/form-data; boundary=xxx,後端 Controller 代碼完全復用,無需區分 Web/App。
ProgressListener 或前端 XMLHttpRequest upload 事件。