跳至主要内容

Content 檢測使用指南

Package: io.leandev.appfuse.content.* 狀態: 穩定


簡介​

Content 模組提供 MIME 類型檢測工具。InputStream / byte[] 以魔術位元組辨識; Path 先使用平台的 Files.probeContentType(),失敗時才依副檔名降級。

核心特色​

特色說明價值
多層檢測策略平台檢測 → 魔術位元組 → 副檔名降級準確率高、容錯性好
內容檢測串流使用魔術位元組;Path 有副檔名降級比只看使用者提供的檔名可靠
跨平台相容自動適配 Linux/macOS/Windows行為一致
常見格式支援圖片、文件、Office、壓縮、影音與程式碼格式開箱即用

與傳統做法的對比​

// ❌ 傳統做法:依賴副檔名,容易被偽造
String filename = uploadedFile.getOriginalFilename();
String mimeType = filename.endsWith(".jpg") ? "image/jpeg" : "application/octet-stream";
// 風險:惡意使用者可上傳 virus.exe 並改名為 image.jpg

// ✅ AppFuse Content:多層檢測,安全可靠
ContentDetector detector = new ContentDetector();
MediaType type = detector.detect(uploadedFile.getInputStream());
// 即使檔名是 .jpg,內容是 .exe 也會被正確識別

快速開始​

檢測檔案路徑​

import io.leandev.appfuse.content.ContentDetector;
import org.springframework.http.MediaType;

ContentDetector detector = new ContentDetector();

Path imagePath = Paths.get("/upload/photo.jpg");
MediaType type = detector.detect(imagePath);

System.out.println(type); // image/jpeg

檢測上傳檔案(Spring MVC)​

@PostMapping("/upload")
public ResponseEntity<String> handleUpload(@RequestParam("file") MultipartFile file)
throws IOException {

ContentDetector detector = new ContentDetector();
MediaType actualType = detector.detect(file.getInputStream());

// 驗證檔案類型
if (!actualType.isCompatibleWith(MediaType.IMAGE_JPEG)
&& !actualType.isCompatibleWith(MediaType.IMAGE_PNG)) {
return ResponseEntity.badRequest()
.body("Only JPEG and PNG images are allowed");
}

// 安全處理檔案...
return ResponseEntity.ok("Upload successful");
}

檢測位元組陣列​

ContentDetector detector = new ContentDetector();

byte[] imageData = downloadFromUrl("https://example.com/image");
MediaType type = detector.detect(imageData);

if (type.getType().equals("image")) {
System.out.println("這是一張圖片:" + type.getSubtype());
}

支援的格式​

圖片格式 (13 種)​

JPEG, PNG, GIF, WebP, BMP, SVG, ICO, TIFF, AVIF, HEIC, HEIF

文檔格式 (7 種)​

PDF, TXT, CSV, XML, JSON, Markdown

Office 格式 (6 種)​

DOC, DOCX, XLS, XLSX, PPT, PPTX

壓縮格式 (5 種)​

ZIP, GZIP, TAR, 7Z, RAR

網頁格式 (7 種)​

HTML, CSS, JavaScript, TypeScript, JSX, TSX

程式碼格式 (10 種)​

Java, Python, Go, Rust, C, C++, Shell, Bash

影音格式 (10 種)​

MP4, AVI, MOV, WebM, MKV, MP3, WAV, OGG, FLAC, AAC

配置檔案 (5 種)​

YAML, TOML, INI, Properties


常見場景​

場景 1: 檔案上傳驗證​

ContentDetector detector = new ContentDetector();
MediaType type = detector.detect(uploadedFile.getInputStream());

if (!type.getType().equals("image")) {
throw new InvalidFileException("Expected image file, but got: " + type);
}

場景 2: 安全檢查(防止檔案偽裝)​

String filename = "document.pdf";
MediaType detectedType = detector.detect(fileBytes);

String expectedType = filename.substring(filename.lastIndexOf('.') + 1);
if (!detectedType.getSubtype().contains(expectedType)) {
logger.warn("File extension mismatch: {} vs {}", filename, detectedType);
// 拒絕可疑檔案或進一步驗證
}

場景 3: 檔案分類處理​

ContentDetector detector = new ContentDetector();
MediaType type = detector.detect(filePath);

switch (type.getType()) {
case "image":
imageProcessor.process(filePath);
break;
case "video":
videoProcessor.process(filePath);
break;
case "application":
if (type.getSubtype().equals("pdf")) {
pdfProcessor.process(filePath);
}
break;
default:
genericProcessor.process(filePath);
}

場景 4: 動態設定 Content-Type​

@GetMapping("/download/{fileId}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileId) throws IOException {

File file = fileService.getFile(fileId);
Resource resource = new FileSystemResource(file);

ContentDetector detector = new ContentDetector();
MediaType mediaType = detector.detect(file.toPath());

return ResponseEntity.ok()
.contentType(mediaType)
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getName() + "\"")
.body(resource);
}

檢測策略​

Path 檢測策略​

1. 平台原生檢測 (Files.probeContentType)
↓ 失敗
2. 副檔名映射查詢
↓ 失敗
3. 預設類型 (application/octet-stream)

InputStream 檢測策略​

1. 魔術位元組分析 (URLConnection.guessContentTypeFromStream)
↓ 失敗
2. 預設類型 (application/octet-stream)

最佳實踐​

✅ 推薦做法​

// 1. 重複使用 detector 實例(無狀態,執行緒安全)
private final ContentDetector detector = new ContentDetector();

// 2. 優先使用串流檢測(更準確)
MediaType type = detector.detect(inputStream);

// 3. 結合檔案驗證
if (type.isCompatibleWith(MediaType.IMAGE_JPEG) ||
type.isCompatibleWith(MediaType.IMAGE_PNG)) {
// 處理圖片
}

❌ 避免的做法​

// 1. 不要只依賴副檔名
String ext = filename.substring(filename.lastIndexOf('.'));
// ❌ 不安全,容易被偽造

// 2. 不要忽略檢測結果
detector.detect(file.toPath()); // ❌ 沒有使用檢測結果
processFile(file);

常見問題​

Q: 不同作業系統結果是否一致?​

A: 大致一致,但因 Files.probeContentType() 實作不同,可能有細微差異。建議在 CI 環境中測試。

Q: 無法識別的檔案會怎樣?​

A: 返回 application/octet-stream,不會拋出異常。

Q: 效能如何?​

A: ContentDetector 內建常用 MIME 類型快取。建議重複使用實例、批次處理時可並行處理。


API 參考​

詳細的類別設計和方法簽名,請參閱 API 參考: Content 或 Javadoc。