【发布时间】:2010-09-08 12:35:40
【问题描述】:
我正在从一项服务下载一些图像,该服务并不总是包含内容类型,也没有为我正在下载的文件提供扩展名(呃,别问了)。
在 .NET 中确定图像格式的最佳方法是什么?
正在读取这些下载图像的应用程序需要具有正确的文件扩展名,否则将彻底崩溃。
【问题讨论】:
标签: .net image content-type mime-types
我正在从一项服务下载一些图像,该服务并不总是包含内容类型,也没有为我正在下载的文件提供扩展名(呃,别问了)。
在 .NET 中确定图像格式的最佳方法是什么?
正在读取这些下载图像的应用程序需要具有正确的文件扩展名,否则将彻底崩溃。
【问题讨论】:
标签: .net image content-type mime-types
尝试将流加载到 System.IO.BinaryReader。
然后您需要参考您需要的每种图像格式的规范,并逐字节加载标题以与规范进行比较。例如这里是PNG specifications
添加:PNG 的实际 file structure。
【讨论】:
所有图像格式都将它们的初始字节设置为特定值:
搜索“jpg 文件格式”,将 jpg 替换为您需要识别的其他文件格式。
按照 Garth 的建议,database of such 'magic numbers' 显示了许多文件的文件类型。如果您必须检测许多不同的文件类型,则值得通过它来查找您需要的信息。如果您确实需要扩展它以涵盖许多文件类型,请查看关联的file command,它实现了正确使用数据库的引擎(对于许多文件格式而言,这不是微不足道的,而且几乎是一个统计过程)
-亚当
【讨论】:
亚当指出的方向完全正确。
如果您想了解如何检测几乎所有文件,请查看 UNIX、Linux 或 Mac OS X 机器上 file 命令背后的数据库。
file 使用“幻数”数据库——亚当列出的那些初始字节——来感知文件的类型。 man file 会告诉你在你的机器上哪里可以找到数据库,例如/usr/share/file/magic。 man magic 会告诉你它的format。
您可以根据您在数据库中看到的内容编写自己的检测代码,使用预打包的库(例如 python-magic),或者 - 如果您真的喜欢冒险 - 实现一个.NET 版本的libmagic。没找到,希望有大佬指点一下。
如果您手边没有 UNIX 机器,数据库如下所示:
# PNG [便携式网络图形,或“PNG 不是 GIF”] 图像 # (Greg Roelofs, newt@uchicago.edu) #(阿尔伯特·卡哈兰,acahalan@cs.uml.edu) # # 137 P N G \r \n ^Z \n [4字节长度] H E A D [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 色祝你好运!
【讨论】:
一个可能更简单的方法是使用 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
【讨论】:
有确定图像 MIMETYPE 的编程方式。
有类System.Drawing.Imaging.ImageCodecInfo。
此类具有属性 MimeType 和 FormatID。它还有一个方法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];
【讨论】:
您可以使用下面的代码而无需引用 System.Drawing 和不必要地创建对象 Image。即使没有 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;
}
【讨论】: