Path path = Paths.get("D:/Downloads/java.txt");
String contentType = Files.probeContentType(path); // 透過檔案內容偵測 MIME
// 結果: text/plain, application/pdf, image/jpeg 等
| 方案 | 原理 | 優點 | 缺點 |
|---|---|---|---|
| Registry (檔案關聯) | 讀取 HKCR\.ext\Content Type | 無額外依賴、系統整合 | 依賴註冊表正確性、無法偵測無副檔名檔案 |
| File Signature (Magic Bytes) | 讀取檔案頭部位元組模式 | 準確、跨平台、無外部依賴 | 需維護簽名資料庫 |
| MimeKit / MimeDetective | 成熟庫,內建大量簽名 | 準確度高、支援超多格式 | 第三方依賴 |
| Windows Shell (IQueryAssociations) | COM 介面查詢系統關聯 | 與總管一致 | 僅 Windows、COM 互通複雜 |
using System;
using System.Collections.Generic;
using System.IO;
public static class MimeDetector
{
// 常見檔案簽名 (Magic Bytes)
private static readonly Dictionary Signatures = new()
{
// 圖片
["image/jpeg"] = new[] { new byte[] { 0xFF, 0xD8, 0xFF } },
["image/png"] = new[] { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } },
["image/gif"] = new[] { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 } },
["image/bmp"] = new[] { new byte[] { 0x42, 0x4D } },
["image/tiff"] = new[] { new byte[] { 0x49, 0x49, 0x2A, 0x00 }, new byte[] { 0x4D, 0x4D, 0x00, 0x2A } },
["image/webp"] = new[] { new byte[] { 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50 } },
// 文件
["application/pdf"] = new[] { new byte[] { 0x25, 0x50, 0x44, 0x46 } }, // %PDF
["application/zip"] = new[] { new byte[] { 0x50, 0x4B, 0x03, 0x04 }, new byte[] { 0x50, 0x4B, 0x05, 0x06 }, new byte[] { 0x50, 0x4B, 0x07, 0x08 } },
["application/msword"] = new[] { new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 } }, // OLE
["application/vnd.openxmlformats-officedocument.wordprocessingml.document"] = new[] { new byte[] { 0x50, 0x4B, 0x03, 0x04 } }, // docx (zip based)
["application/vnd.ms-excel"] = new[] { new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 } },
["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"] = new[] { new byte[] { 0x50, 0x4B, 0x03, 0x04 } },
// 影音
["video/mp4"] = new[] { new byte[] { 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70 } }, // ftyp
["audio/mpeg"] = new[] { new byte[] { 0xFF, 0xFB }, new byte[] { 0xFF, 0xF3 }, new byte[] { 0x49, 0x44, 0x33 } }, // MP3 + ID3
// 文字 (無固定簽名,通常靠副檔名或內容分析)
["text/plain"] = Array.Empty(),
};
public static string ProbeContentType(string filePath)
{
if (!File.Exists(filePath)) return "application/octet-stream";
var ext = Path.GetExtension(filePath).ToLowerInvariant();
// 1. 嘗試 Magic Bytes 偵測
using (var fs = File.OpenRead(filePath))
{
var header = new byte[12]; // 讀取前 12 bytes 足夠大多數格式
int read = fs.Read(header, 0, header.Length);
if (read == 0) return "application/octet-stream";
foreach (var kv in Signatures)
{
foreach (var sig in kv.Value)
{
if (sig.Length == 0) continue; // text/plain 等無簽名
if (read >= sig.Length && Matches(header, sig))
return kv.Key;
}
}
}
// 2. 回退:副檔名對照表
return ExtensionMap.TryGetValue(ext, out var mime) ? mime : "application/octet-stream";
}
private static bool Matches(byte[] header, byte[] signature)
{
for (int i = 0; i < signature.Length; i++)
if (header[i] != signature[i]) return false;
return true;
}
// 常見副檔名對照
private static readonly Dictionary ExtensionMap = new()
{
[".txt"] = "text/plain", [".csv"] = "text/csv", [".json"] = "application/json",
[".xml"] = "application/xml", [".html"] = "text/html", [".htm"] = "text/html",
[".css"] = "text/css", [".js"] = "application/javascript", [".ts"] = "application/typescript",
[".pdf"] = "application/pdf", [".doc"] = "application/msword", [".docx"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
[".xls"] = "application/vnd.ms-excel", [".xlsx"] = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
[".ppt"] = "application/vnd.ms-powerpoint", [".pptx"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation",
[".zip"] = "application/zip", [".rar"] = "application/x-rar-compressed", [".7z"] = "application/x-7z-compressed",
[".jpg"] = "image/jpeg", [".jpeg"] = "image/jpeg", [".png"] = "image/png", [".gif"] = "image/gif", [".bmp"] = "image/bmp", [".webp"] = "image/webp",
[".mp3"] = "audio/mpeg", [".wav"] = "audio/wav", [".mp4"] = "video/mp4", [".avi"] = "video/x-msvideo",
[".exe"] = "application/x-msdownload", [".dll"] = "application/x-msdownload",
};
}
using Microsoft.Win32;
public static string GetMimeFromRegistry(string extension)
{
if (string.IsNullOrEmpty(extension)) return "application/octet-stream";
if (!extension.StartsWith(".")) extension = "." + extension;
using (var key = Registry.ClassesRoot.OpenSubKey(extension))
{
if (key?.GetValue("Content Type") is string mime)
return mime;
}
return "application/octet-stream";
}
// NuGet: MimeKit
using MimeKit;
public static string ProbeWithMimeKit(string filePath)
{
var provider = new MimeKit.MimeTypes();
return provider.GetMimeType(filePath); // 自動結合副檔名與內容偵測
}
MimeKit,內建 1000+ 格式,持續更新
var files = new[] { "test.pdf", "image.png", "data.json", "unknown.xyz" };
foreach (var f in files)
{
Console.WriteLine($"{f} => {MimeDetector.ProbeContentType(f)}");
}
// 輸出:
// test.pdf => application/pdf
// image.png => image/png
// data.json => application/json
// unknown.xyz => application/octet-stream
實作 probeContentType 核心在於檔案簽名優先、副檔名輔助,根據專案需求選擇零依賴手寫版或成熟庫版本。