<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version> <!-- 建議升級至 1.4 修復安全漏洞 -->
</dependency>
<!-- Spring MVC 已傳遞依賴 commons-io -->
<!-- dispatcher-servlet.xml -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大上傳 10MB -->
<property name="maxUploadSize" value="10485760" />
<!-- 內存臨界值 100KB,超過寫入臨時文件 -->
<property name="maxInMemorySize" value="102400" />
<!-- 臨時文件存放路徑 -->
<property name="uploadTempDir" value="/tmp/uploads" />
<!-- 編碼設置 -->
<property name="defaultEncoding" value="UTF-8" />
</bean>
關鍵屬性:
maxUploadSize:總請求大小限制(含表單字段),-1 為無限制maxInMemorySize:內存緩衝閾值,超過寫磁盤臨時文件,防 OOMresolveLazily:是否延遲解析(默認 false),true 可在 Controller 手動控制<form method="POST" action="/upload.do" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="text" name="description" />
<button type="submit">上傳</button>
</form>
enctype="multipart/form-data" 必須設置,否則請求體不會被解析為多部分,MultipartFile 為 null。
@Controller
@RequestMapping("/file")
public class FileUploadController {
private static final String UPLOAD_DIR = "WEB-INF/upload";
private static final long MAX_SIZE = 10 * 1024 * 1024; // 10MB
private static final Set ALLOWED_TYPES = Set.of(
"image/jpeg", "image/png", "application/pdf", "text/plain"
);
@PostMapping("/upload.do")
public String upload(@RequestParam("file") MultipartFile file,
@RequestParam(value = "desc", required = false) String desc,
RedirectAttributes redirectAttrs) {
// 1. 基礎校驗
if (file.isEmpty()) {
redirectAttrs.addFlashAttribute("error", "請選擇文件");
return "redirect:/upload.html";
}
if (file.getSize() > MAX_SIZE) {
redirectAttrs.addFlashAttribute("error", "文件超過 10MB 限制");
return "redirect:/upload.html";
}
if (!ALLOWED_TYPES.contains(file.getContentType())) {
redirectAttrs.addFlashAttribute("error", "不支持的文件類型");
return "redirect:/upload.html";
}
// 2. 生成安全文件名:UUID + 原始後綴
String originalName = file.getOriginalFilename();
String suffix = originalName.substring(originalName.lastIndexOf("."));
String savedName = UUID.randomUUID() + suffix;
// 3. 目標路徑:實際部署路徑 + 相對目錄
String realPath = request.getServletContext().getRealPath(UPLOAD_DIR);
File destDir = new File(realPath);
if (!destDir.exists()) destDir.mkdirs();
File destFile = new File(destDir, savedName);
// 4. 保存文件:transferTo 內部優化(大文件直接 rename,小文件流拷貝)
try {
file.transferTo(destFile);
log.info("文件上傳成功: {} -> {}", originalName, destFile.getAbsolutePath());
redirectAttrs.addFlashAttribute("msg", "上傳成功: " + savedName);
} catch (IOException e) {
log.error("文件保存失敗", e);
redirectAttrs.addFlashAttribute("error", "服務器內部錯誤");
}
return "redirect:/upload.html";
}
}
| 方法 | 說明 |
|---|---|
| getOriginalFilename() | 客戶端原始文件名(可能含路徑,需 FilenameUtils.getName() 清洗) |
| getContentType() | MIME 類型(瀏覽器根據後綴推測,不可完全信任) |
| getSize() | 文件字節大小 |
| isEmpty() | 大小為 0 |
| getInputStream() | 獲取輸入流(需手動關閉) |
| transferTo(File dest) | 推薦:高性能保存,自動處理內存/臨時文件 |
| getBytes() | 全量讀入 byte[](大文件慎用,OOM 風險) |
spring:
servlet:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 100MB
file-size-threshold: 100KB
location: /tmp/uploads
無需手動定義 Bean,自動裝配 StandardServletMultipartResolver(基於 Servlet 3.0 Part API,無需 commons-fileupload)。
FilenameUtils.getName() 截取純文件名,禁止 `../`