在C#中验证文件中的图像




.net image (10)

一种支持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();
        }
    }
}

我正在从文件加载图像,我想知道如何在从文件中完全读取之前验证图像。

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

当image.jpg不是真正的jpg时会出现问题。 例如,如果我创建一个空文本文件并将其重命名为image.jpg,则在加载image.jpg时将抛出OutOfMemory Exception。

我正在寻找一个功能,它将在给定图像的流或文件路径的情况下验证图像。

示例函数原型

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

我拿了Semicolon的答案并转换为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

注意到上述所有功能的几个问题。 首先 - Image.FromFile打开给定的图像,然后将导致打开文件错误谁想要出于任何原因打开给定的图像文件。 甚至应用程序本身 - 所以我使用Image.FromStream切换。

在你将api - 异常类型更改从OutOfMemoryException转换为ArgumentException之后,我发现某些原因尚不清楚。 (可能是.net框架错误?)

此外,如果.net将添加比目前更多的图像文件格式支持,我们将按功能检查 - 首先尝试加载图像,如果然后失败 - 只有在报告错误之后才有意义。

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

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

我会创建一个方法,如:

Image openImage(string filename);

我在其中处理异常。 如果返回的值为Null,则文件名/类型无效。


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


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

您必须释放创建的图像。 否则,当您多次调用此函数时,这会抛出OutOfMemoryException,因为系统资源不足,而不是因为图像损坏导致结果不正确,并且如果在此步骤之后删除图像,则可能会删除好的。


你可以通过嗅探标题进行粗略的输入。

这意味着您实现的每种文件格式都需要具有可识别的标头...

JPEG:前4个字节是FF D8 FF E0(实际上只有前两个字节可以用于非jfif jpeg, here更多信息)。

GIF:前6个字节是“GIF87a”或“GIF89a”( here更多信息)

PNG:前8个字​​节是:89 50 4E 47 0D 0A 1A 0A(更多信息here

TIFF:前4个字节是:II42或MM42( here更多信息)

等等...您可以找到您关心的任何图形格式的标题/格式信息,并根据需要添加到它处理的内容中。 这不会做的是告诉你文件是否是该类型的有效版本,但它会给你一个关于“图像不是图像?”的提示。 它仍然可能是一个损坏或不完整的图像,因此在打开时会崩溃,所以仍然需要尝试捕捉.FromFile调用。


JPEG没有正式的标头定义,但它们确实有少量可用的元数据。

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

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

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

这至少可以让你更精确。

或者您可以捕获异常并继续前进,但我认为您想要挑战:)


这应该可以解决问题 - 您不必从标头中读取原始字节:

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

当然,您也应该捕获OutOfMemoryException,如果文件根本不是图像,这将节省您的时间。

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

注意,您必须在ImageFormat对象上使用.Equals()而不是==(它不是枚举),因为运算符==没有重载以调用Equals方法。


好吧,我继续编写了一组函数来解决问题。 它首先检查标头,然后尝试在try / catch块中加载图像。 它仅检查GIF,BMP,JPG和PNG文件。 您可以通过向imageHeaders添加标题来轻松添加更多类型。

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






file-io