从C#中的文件validation图像

我正在从一个文件加载图像,我想知道如何validation图像,然后才能从文件中完全读取图像。

string filePath = "image.jpg"; Image newImage = Image.FromFile(filePath); 

这个问题发生在image.jpg不是真正的JPG格式时。 例如,如果我创build一个空的文本文件并将其重命名为image.jpg,则会在image.jpg加载时引发OutOfMemoryexception。

我正在寻找一个函数来validation给定的图像stream或文件path的图像。

示例函数原型

 bool IsValidImage(string fileName); bool IsValidImage(Stream imageStream); 

JPEG没有正式的标题定义,但是它们确实有一小部分可以使用的元数据。

  • 偏移量0(两个字节):JPEG SOI标记(FFD8hex)
  • 偏移量2(两个字节):以像素为单位的图像宽度
  • 偏移4(2字节):图像高度(以像素为单位)
  • 偏移量6(字节):组件数(1 =灰度,3 = RGB)

之后还有其他一些事情,但这些并不重要。

您可以使用二进制stream打开文件,并读取此初始数据,并确保OffSet 0为0,OffSet 6为1,2或3。

那至less会让你稍微精确些。

或者你可以捕捉exception并继续前进,但是我以为你想要一个挑战:)

这是我的形象检查。 我不能依靠文件扩展名,必须自己检查格式。 我从字节数组加载WPF中的BitmapImages,并不知道前面的格式。 WPF检测格式正常,但不会告诉你BitmapImage对象的图像格式(至less我不知道这个属性)。 而且我不想用System.Drawing再次加载图像来检测格式。 这个解决scheme很快,对我来说工作得很好。

 public enum ImageFormat { bmp, jpeg, gif, tiff, png, unknown } public static ImageFormat GetImageFormat(byte[] bytes) { // 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 if (bmp.SequenceEqual(bytes.Take(bmp.Length))) return ImageFormat.bmp; if (gif.SequenceEqual(bytes.Take(gif.Length))) return ImageFormat.gif; if (png.SequenceEqual(bytes.Take(png.Length))) return ImageFormat.png; if (tiff.SequenceEqual(bytes.Take(tiff.Length))) return ImageFormat.tiff; if (tiff2.SequenceEqual(bytes.Take(tiff2.Length))) return ImageFormat.tiff; if (jpeg.SequenceEqual(bytes.Take(jpeg.Length))) return ImageFormat.jpeg; if (jpeg2.SequenceEqual(bytes.Take(jpeg2.Length))) return ImageFormat.jpeg; return ImageFormat.unknown; } 

使用Windows窗体:

 bool IsValidImage(string filename) { try { using(Image newImage = Image.FromFile(filename)) {} } catch (OutOfMemoryException ex) { //The file does not have a valid image format. //-or- GDI+ does not support the pixel format of the file return false; } return true; } 

否则,如果您使用的是WPF ,则可以执行以下操作:

 bool IsValidImage(string filename) { try { using(BitmapImage newImage = new BitmapImage(filename)) {} } catch(NotSupportedException) { // System.NotSupportedException: // No imaging component suitable to complete this operation was found. return false; } return true; } 

您必须释放所创build的图像。 否则,当你调用这个函数的次数很多时,这会抛出OutOfMemoryException,因为系统资源耗尽,而不是因为图像损坏而产生不正确的结果,如果在这一步之后删除图像,你可能会被删除好的。

那么,我继续编写一套函数来解决这个问题。 它首先检查标题,然后尝试在try / catch块中加载图像。 它只检查GIF,BMP,JPG和PNG文件。 您可以通过向imageHeaders添加标题来轻松添加更多types。

 static bool IsValidImage(string filePath) { return File.Exists(filePath) && IsValidImage(new FileStream(filePath, FileMode.Open, FileAccess.Read)); } static bool IsValidImage(Stream imageStream) { if(imageStream.Length > 0) { byte[] header = new byte[4]; // Change size if needed. string[] imageHeaders = new[]{ "\xFF\xD8", // JPEG "BM", // BMP "GIF", // GIF Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71})}; // PNG imageStream.Read(header, 0, header.Length); bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0; if (isImageHeader == true) { try { Image.FromStream(imageStream).Dispose(); imageStream.Close(); return true; } catch { } } } imageStream.Close(); return false; } 

您可以通过嗅探标题来进行粗略的input。

这意味着你实现的每个文件格式都需要有一个可识别的头文件。

JPEG:前4个字节是FF D8 FF E0(实际上只有前两个字节会用于非jfif jpeg,更多信息请见这里 )。

GIF:前6个字节是“GIF87a”或“GIF89a”(更多信息请点击这里 )

PNG:前8个字​​节是:89 50 4E 47 0D 0A 1A 0A(更多信息请点击这里 )

TIFF:前4个字节是:II42或MM42(更多信息请点击这里 )

等等…你可以find关于你关心的任何graphics格式的头文件/格式信息,并根据需要添加它处理的东西。 这不会做什么,是告诉你,如果该文件是该types的有效版本,但它会给你一个关于“图像不是图像?”的提示。 它仍然可能是一个腐败或不完整的形象,并因此打开时崩溃,所以试图抓住.FromFile调用仍然是需要的。

这应该做的伎俩 – 你不必读取标题的原始字节:

 using(Image test = Image.FromFile(filePath)) { bool isJpeg = (test.RawFormat.Equals(ImageFormat.Jpeg)); } 

当然,你也应该捕获OutOfMemoryExceptionexception,如果这个文件根本不是图像的话,这将会节省你的时间。

而且,ImageFormat还为GDI +支持的所有其他主要图像types预先设置了项目。

请注意,您必须在ImageFormat对象(不是枚举)上使用.Equals()而不是==,因为不会重载operator ==以调用Equals方法。

也支持Tiff和Jpeg的方法

 private bool IsValidImage(string filename) { Stream imageStream = null; try { imageStream = new FileStream(filename, FileMode.Open); if (imageStream.Length > 0) { byte[] header = new byte[30]; // Change size if needed. string[] imageHeaders = new[] { "BM", // BMP "GIF", // GIF Encoding.ASCII.GetString(new byte[]{137, 80, 78, 71}),// PNG "MM\x00\x2a", // TIFF "II\x2a\x00" // TIFF }; imageStream.Read(header, 0, header.Length); bool isImageHeader = imageHeaders.Count(str => Encoding.ASCII.GetString(header).StartsWith(str)) > 0; if (imageStream != null) { imageStream.Close(); imageStream.Dispose(); imageStream = null; } if (isImageHeader == false) { //Verify if is jpeg using (BinaryReader br = new BinaryReader(File.Open(filename, FileMode.Open))) { UInt16 soi = br.ReadUInt16(); // Start of Image (SOI) marker (FFD8) UInt16 jfif = br.ReadUInt16(); // JFIF marker return soi == 0xd8ff && (jfif == 0xe0ff || jfif == 57855); } } return isImageHeader; } return false; } catch { return false; } finally { if (imageStream != null) { imageStream.Close(); imageStream.Dispose(); } } } 

我会创build一个方法,如:

 Image openImage(string filename); 

我在其中处理exception。 如果返回的值是Null,则有一个无效的文件名/types。

我采取了分号的答案,并转换为VB:

 Private Function IsValidImage(imageStream As System.IO.Stream) As Boolean If (imageStream.Length = 0) Then isvalidimage = False Exit Function End If Dim pngByte() As Byte = New Byte() {137, 80, 78, 71} Dim pngHeader As String = System.Text.Encoding.ASCII.GetString(pngByte) Dim jpgByte() As Byte = New Byte() {255, 216} Dim jpgHeader As String = System.Text.Encoding.ASCII.GetString(jpgByte) Dim bmpHeader As String = "BM" Dim gifHeader As String = "GIF" Dim header(3) As Byte Dim imageHeaders As String() = New String() {jpgHeader, bmpHeader, gifHeader, pngHeader} imageStream.Read(header, 0, header.Length) Dim isImageHeader As Boolean = imageHeaders.Count(Function(str) System.Text.Encoding.ASCII.GetString(header).StartsWith(str)) > 0 If (isImageHeader) Then Try System.Drawing.Image.FromStream(imageStream).Dispose() imageStream.Close() IsValidImage = True Exit Function Catch ex As Exception System.Diagnostics.Debug.WriteLine("Not an image") End Try Else System.Diagnostics.Debug.WriteLine("Not an image") End If imageStream.Close() IsValidImage = False End Function 

您可以读取stream的前几个字节,并将它们与JPEG的魔术字节字节进行比较。

如果您需要为其他操作和/或其他文件types(例如PSD)读取数据,稍后使用Image.FromStream函数并不一定是一个好的理念。

注意到以上所有function的一些问题。 首先 – Image.FromFile打开给定的图像,之后会导致打开文件错误谁想要打开给定的图像文件出于任何原因。 即使应用程序本身 – 所以我已经切换使用Image.FromStream。

切换api之后 – exceptiontypes从OutOfMemoryException更改为ArgumentException对于我有些不清楚的原因。 (可能是.net框架错误?)

此外,如果.net将添加更多的图像文件格式支持比目前我们将按function检查 – 它是有道理的,首先尝试加载图像,如果只是如果失败 – 只有之后,报告错误。

所以我的代码看起来像这样:

 try { using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { Image im = Image.FromStream(stream); // Do something with image if needed. } } catch (ArgumentException) { if( !IsValidImageFormat(path) ) return SetLastError("File '" + fileName + "' is not a valid image"); throw; } 

哪里:

 /// <summary> /// Check if we have valid Image file format. /// </summary> /// <param name="path"></param> /// <returns>true if it's image file</returns> public static bool IsValidImageFormat( String path ) { using ( FileStream fs = File.OpenRead(path) ) { byte[] header = new byte[10]; fs.Read(header, 0, 10); foreach ( var pattern in new byte[][] { Encoding.ASCII.GetBytes("BM"), Encoding.ASCII.GetBytes("GIF"), new byte[] { 137, 80, 78, 71 }, // PNG new byte[] { 73, 73, 42 }, // TIFF new byte[] { 77, 77, 42 }, // TIFF new byte[] { 255, 216, 255, 224 }, // jpeg new byte[] { 255, 216, 255, 225 } // jpeg canon } ) { if (pattern.SequenceEqual(header.Take(pattern.Length))) return true; } } return false; } //IsValidImageFormat