拡張子からのMIMEタイプの取得

Windowsの場合、レジストリのHKEY_CLASSES_ROOTに、拡張子毎にアプリケーションの関連付けなどの情報が格納されている。 目的の拡張子をキーとしたノードにContent Typeの値が登録されている場合は、その値を使用できる。

レジストリを参照して拡張子からMIMEタイプを取得する
using System;
using Microsoft.Win32;

class Sample {
  private static string GetMimeTypeByExtension(string extension)
  {
    const string defaultType = "application/octet-stream";

    var key = Registry.ClassesRoot.OpenSubKey(extension);

    if (key == null)
      return defaultType;

    var mimeType = key.GetValue("Content Type");

    if (mimeType == null)
      return defaultType;
    else
      return (string)mimeType;
  }

  static void Main(string[] args)
  {
    var extensions = new[] {
      ".txt", ".eml", ".html",
      ".jpg", ".bmp", ".wav",
      ".mp4",
      ".exe", ".dll", ".bin",
      ".zip", ".tar.gz",
    };

    foreach (var extension in extensions) {
      Console.WriteLine("{0} => {1}", extension, GetMimeTypeByExtension(extension));
    }
  }
}
レジストリを参照して拡張子からMIMEタイプを取得する
Imports System
Imports Microsoft.Win32

Class Sample
  Private Shared Function GetMimeTypeByExtension(ByVal extension As String) As String
    Const defaultType As String = "application/octet-stream"

    Dim key As RegistryKey = Registry.ClassesRoot.OpenSubKey(extension)

    If key Is Nothing Then Return defaultType

    Dim mimeType As Object = key.GetValue("Content Type")

    If mimeType Is Nothing Then
      Return defaultType
    Else
      Return CStr(mimeType)
    End If
  End Function

  Shared Sub Main()
    Dim extensions As String() = New String() {
      ".txt", ".eml", ".html", _
      ".jpg", ".bmp", ".wav", _
      ".mp4", _
      ".exe", ".dll", ".bin", _
      ".zip", ".tar.gz" _
    }

    For Each extension As String In extensions
      Console.WriteLine("{0} => {1}", extension, GetMimeTypeByExtension(extension))
    Next
  End Sub
End Class
実行結果
.txt => text/plain
.eml => message/rfc822
.html => text/html
.jpg => image/jpeg
.bmp => image/bmp
.wav => audio/wav
.mp4 => video/mp4
.exe => application/x-msdownload
.dll => application/x-msdownload
.bin => application/octet-stream
.zip => application/x-zip-compressed
.tar.gz => application/octet-stream

データストリームからのMIMEタイプの判定

FindMimeFromData関数を用いることで、データストリームからMIMEタイプを推定することが出来る。

(この項の内容は検証が不十分です。 適用に際してはリンク先のドキュメントを参照してください。)

FindMimeFromDataを使ってデータストリームからMIMEタイプを推定する
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

class Sample {
  private enum FMFD : uint {
    FMFD_DEFAULT              = 0,
    FMFD_URLASFILENAME        = 1 << 0,
    FMFD_ENABLEMIMESNIFFING   = 1 << 1,
    FMFD_IGNOREMIMETEXTPLAIN  = 1 << 2,
    FMFD_SERVERMIME           = 1 << 3, // ?
  }

  [DllImport("urlmon")] private static extern
  uint FindMimeFromData(IntPtr pBC,
                       IntPtr pwzUrl,
                       byte[] buffer,
                       int cbSize,
                       [In, MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
                       FMFD dwMimeFlags,
                       [MarshalAs(UnmanagedType.LPWStr)] out string ppwzMimeOut,
                       uint dwReserved);

  private static string GetMimeTypeByMagic(byte[] magic, string mimeProposed)
  {
    const uint E_INVALIDARG = 0x80070057;
    string mime;

    var ret = FindMimeFromData(IntPtr.Zero, IntPtr.Zero, magic, magic.Length, mimeProposed, FMFD.FMFD_DEFAULT, out mime, 0);

    if (ret == 0)
      return mime;
    else if (ret == E_INVALIDARG)
      throw new ArgumentException();
    else
      return null;
  }

  static void Main()
  {
    var image = new Bitmap(8, 8);

    // イメージ
    foreach (var format in new[] {
      System.Drawing.Imaging.ImageFormat.Bmp,
      System.Drawing.Imaging.ImageFormat.Jpeg,
      System.Drawing.Imaging.ImageFormat.Png,
    }) {
      using (var stream = new MemoryStream()) {
        image.Save(stream, format);

        Console.WriteLine("{0} => {1}", format, GetMimeTypeByMagic(stream.ToArray(), null));
      }
    }

    // プレーンテキスト
    var text = "Hello, world.\r\n";

    Console.WriteLine("plain text => {0}", GetMimeTypeByMagic(Encoding.Default.GetBytes(text), "text/plain"));

    // XML (Atomフィード)
    text = @"<?xml version=""1.0""?>
  <feed xmlns=""http://www.w3.org/2005/Atom"">
  </feed>
  </xml>";

    Console.WriteLine("XML => {0}", GetMimeTypeByMagic(Encoding.Default.GetBytes(text), "text/plain"));

    // HTML
    text = @"<html><body>HTML</body></html>";

    Console.WriteLine("HTML => {0}", GetMimeTypeByMagic(Encoding.Default.GetBytes(text), "text/plain"));

    // 256バイトのランダムなストリーム
    var random = new Random();

    for (var act = 0; act < 3; act++) {
      var stream = new byte[0x100];

      random.NextBytes(stream);

      Console.WriteLine("random => {0}", GetMimeTypeByMagic(stream, "application/octet-stream"));
    }
  }
}
実行結果
Bmp => image/bmp
Jpeg => image/pjpeg
Png => image/x-png
plain text => text/plain
XML => text/xml
HTML => text/html
random => application/octet-stream
random => application/octet-stream
random => application/octet-stream

FindMimeFromDataは、渡されたデータストリームをスキャンしてMIME Type Detection in Internet ExplorerのKnown MIME Typesにあるタイプかどうかテストする。 このページには判定方法についての詳細が書かれている。

FindMimeFromDataは、通常最初の256バイトの内容を重視するとしているので、例えば256バイトを越えるファイルの内容をすべて渡すのは無駄と思われる。