50

我正在从一项服务下载一些图像,该服务并不总是包含内容类型并且不为我正在下载的文件提供扩展名(呃,不要问)。

在 .NET 中确定图像格式的最佳方法是什么?

正在读取这些下载图像的应用程序需要具有适当的文件扩展名,否则将彻底崩溃。

4

6 回答 6

55

一种可能更简单的方法是使用 Image.FromFile() 然后使用 RawFormat 属性,因为它已经知道最常见格式的标头中的魔术位,如下所示:

Image i = Image.FromFile("c:\\foo");
if (System.Drawing.Imaging.ImageFormat.Jpeg.Equals(i.RawFormat)) 
    MessageBox.Show("JPEG");
else if (System.Drawing.Imaging.ImageFormat.Gif.Equals(i.RawFormat))
    MessageBox.Show("GIF");
//Same for the rest of the formats
于 2008-09-11T06:11:18.593 回答
28

您可以在不引用 System.Drawing 和不必要的对象图像创建的情况下使用下面的代码。即使没有 System.IO 的流和引用,您也可以使用Alex解决方案。

public enum ImageFormat
{
    bmp,
    jpeg,
    gif,
    tiff,
    png,
    unknown
}

public static ImageFormat GetImageFormat(Stream stream)
{
    // see http://www.mikekunz.com/image_file_header.html
    var bmp = Encoding.ASCII.GetBytes("BM");     // BMP
    var gif = Encoding.ASCII.GetBytes("GIF");    // GIF
    var png = new byte[] { 137, 80, 78, 71 };    // PNG
    var tiff = new byte[] { 73, 73, 42 };         // TIFF
    var tiff2 = new byte[] { 77, 77, 42 };         // TIFF
    var jpeg = new byte[] { 255, 216, 255, 224 }; // jpeg
    var jpeg2 = new byte[] { 255, 216, 255, 225 }; // jpeg canon

    var buffer = new byte[4];
    stream.Read(buffer, 0, buffer.Length);

    if (bmp.SequenceEqual(buffer.Take(bmp.Length)))
        return ImageFormat.bmp;

    if (gif.SequenceEqual(buffer.Take(gif.Length)))
        return ImageFormat.gif;

    if (png.SequenceEqual(buffer.Take(png.Length)))
        return ImageFormat.png;

    if (tiff.SequenceEqual(buffer.Take(tiff.Length)))
        return ImageFormat.tiff;

    if (tiff2.SequenceEqual(buffer.Take(tiff2.Length)))
        return ImageFormat.tiff;

    if (jpeg.SequenceEqual(buffer.Take(jpeg.Length)))
        return ImageFormat.jpeg;

    if (jpeg2.SequenceEqual(buffer.Take(jpeg2.Length)))
        return ImageFormat.jpeg;

    return ImageFormat.unknown;
}
于 2012-09-16T22:01:44.430 回答
22

所有图像格式都将它们的初始字节设置为特定值:

搜索“jpg 文件格式”,将 jpg 替换为您需要识别的其他文件格式。

正如 Garth 建议的那样,有一个包含此类“神奇数字”的数据库,可以显示许多文件的文件类型。如果您必须检测许多不同的文件类型,则值得通过它来查找所需的信息。如果您确实需要扩展它以涵盖许多文件类型,请查看实现引擎以正确使用数据库的关联文件命令(对于许多文件格式而言,这不是微不足道的,并且几乎是一个统计过程)

-亚当

于 2008-09-11T05:57:01.657 回答
8

亚当指出了完全正确的方向。

如果您想了解如何感知几乎所有文件file,请查看UNIX、Linux 或 Mac OS X 机器上命令背后的数据库。

file使用“幻数”数据库——亚当列出的那些初始字节——来感知文件的类型。man file会告诉你在你的机器上哪里可以找到数据库,例如/usr/share/file/magic. man magic会告诉你它的格式

您可以根据在数据库中看到的内容编写自己的检测代码,使用预打包的库(例如python-magic),或者——如果你真的很冒险——实现 .NET 版本的libmagic. 没找到,希望有大佬指点一下。

如果您手边没有 UNIX 机器,数据库将如下所示:

# PNG [便携式网络图形,或“PNG 不是 GIF”] 图像
# (Greg Roelofs, newt@uchicago.edu)
#(阿尔伯特·卡哈兰,acahalan@cs.uml.edu)
#
# 137 PNG \r \n ^Z \n [4字节长度] HEAD [HEAD数据] [HEAD crc] ...
#
0 字符串 \x89PNG PNG 图像数据,
>4 属于 !0x0d0a1a0a 损坏,
>4 属于 0x0d0a1a0a
>>16 属于 x %ld x
>>20 属于 x %ld,
>>24 字节 x %d 位
>>25 字节 0 灰度,
>>25 字节 2 \b/颜色 RGB,
>>25 字节 3 颜色图,
>>25 字节 4 灰度+alpha,
>>25 字节 6 \b/颜色 RGBA,
#>>26 字节 0 放气/32K,
>>28 字节 0 非隔行扫描
>>28 字节 1 隔行扫描
1 字符串 PNG PNG 图像数据,已损坏

# 动图
0 字符串 GIF8 GIF 图像数据
>4 字符串 7a \b,版本 8%s,
>4 字符串 9a \b,版本 8%s,
>6 leshort >0 %hd x
>8 leshort >0 %hd
#>10 字节 &0x80 颜色映射,
#>10 字节&0x07 =0x00 2 种颜色
#>10 字节&0x07 =0x01 4 色
#>10 字节&0x07 =0x02 8 色
#>10 字节&0x07 =0x03 16 色
#>10字节&0x07 =0x04 32色
#>10 字节&0x07 =0x05 64 色
#>10 字节&0x07 =0x06 128 色
#>10 字节&0x07 =0x07 256 色

祝你好运!

于 2008-09-11T06:01:23.487 回答
3

有确定图像 MIMETYPE 的编程方式。

有类System.Drawing.Imaging.ImageCodecInfo

此类具有属性MimeTypeFormatID。它还有一个GetImageEncoders方法,它返回所有图像编码器的集合。创建由格式 id 索引的 mime 类型的字典很容易。

System.Drawing.Image类具有System.Drawing.Imaging.ImageFormat类型的属性RawFormat ,其属性Guid等效于System.Drawing.Imaging.ImageCodecInfo类的属性FormatID,这是从字典中获取 MIMETYPE 的关键。

例子:

创建 mime 类型字典的静态方法

static Dictionary<Guid, string> GetImageFormatMimeTypeIndex()
{
  Dictionary<Guid, string> ret = new Dictionary<Guid, string>();

  var encoders = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();

  foreach(var e in encoders)
  {
    ret.Add(e.FormatID, e.MimeType);
  }

  return ret;
}

采用:

Dictionary<Guid, string> mimeTypeIndex = GetImageFormatMimeTypeIndex();

FileStream imgStream = File.OpenRead(path);
var image = System.Drawing.Image.FromStream(imgStream);
string mimeType = mimeTypeIndex[image.RawFormat.Guid];
于 2012-08-21T09:18:22.977 回答
0

尝试将流加载到 System.IO.BinaryReader。

然后,您需要参考您需要的每种图像格式的规范,并逐字节加载标题以与规范进行比较。例如这里是PNG规范

补充:PNG的实际文件结构

于 2008-09-11T05:54:37.573 回答