Sponsored Link

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

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

private static string GetMimeTypeByExtension(string extension)
{
  const string @default = "application/octet-stream";

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

  if (key == null)
    return @default;

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

  if (mimeType == null)
    return @default;
  else
    return (string)mimeType;
}

public 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));
  }
}
 {1}", extension, GetMimeTypeByExtension(extension));
  }
}]]>
実行結果
.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タイプを推定することが出来る。

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

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

public 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"));
  }
}
 {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 = @"


";

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

  // HTML
  text = @"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バイトを越えるファイルの内容をすべて渡すのは無駄と思われる。