首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 實作 Java 7 Files.probeContentType:MIME 偵測與檔案類型識別

     編輯:關於C#

Java 7 原生功能


Path path = Paths.get("D:/Downloads/java.txt");
String contentType = Files.probeContentType(path); // 透過檔案內容偵測 MIME
// 結果: text/plain, application/pdf, image/jpeg 等

C# 實作方案對比

方案原理優點缺點
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",
    };
}

方案二:Windows 註冊表查詢 (僅 Windows、需副檔名)


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";
}

方案三:MimeKit (生產級、NuGet 套件)


// NuGet: MimeKit
using MimeKit;

public static string ProbeWithMimeKit(string filePath)
{
    var provider = new MimeKit.MimeTypes();
    return provider.GetMimeType(filePath); // 自動結合副檔名與內容偵測
}

使用建議

  1. 跨平台/無依賴:方案一 (Magic Bytes),可按需擴充簽名表
  2. Windows 桌面應用:方案二 (Registry) 做快速預設,關鍵檔案再用方案一驗證
  3. 企業級/高準確度:方案三 MimeKit,內建 1000+ 格式,持續更新
  4. 文字檔偵測:無 Magic Bytes,結合副檔名 + UTF-8/ASCII 內容啟發式判斷

測試範例


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 核心在於檔案簽名優先、副檔名輔助,根據專案需求選擇零依賴手寫版或成熟庫版本。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved