Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 验证C中文件中的图像#_C#_.net_Image_File Io - Fatal编程技术网

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

C# 验证C中文件中的图像#,c#,.net,image,file-io,C#,.net,Image,File Io,我正在从文件加载一个图像,我想知道如何在从文件中完全读取图像之前验证图像 string filePath = "image.jpg"; Image newImage = Image.FromFile(filePath); 当image.jpg不是真正的jpg时,就会出现问题。例如,如果创建一个空文本文件并将其重命名为image.jpg,则加载image.jpg时将引发OutOfMemory异常 我正在寻找一个函数,该函数将验证给定图像流或图像文件路径的图像 示例函数原型 bool IsVali

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

string filePath = "image.jpg";
Image newImage = Image.FromFile(filePath);
当image.jpg不是真正的jpg时,就会出现问题。例如,如果创建一个空文本文件并将其重命名为image.jpg,则加载image.jpg时将引发OutOfMemory异常

我正在寻找一个函数,该函数将验证给定图像流或图像文件路径的图像

示例函数原型

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

我将创建一个如下方法:

Image openImage(string filename);

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

JPEG没有正式的头定义,但它们有少量可供使用的元数据

  • 偏移量0(两个字节):JPEG SOI标记(FFD8十六进制)
  • 偏移量2(两个字节):以像素为单位的图像宽度
  • 偏移量4(两个字节):以像素为单位的图像高度
  • 偏移量6(字节):组件数量(1=灰度,3=RGB)
之后还有几件事,但这些都不重要

您可以使用二进制流打开该文件,并读取该初始数据,并确保偏移量0为0,偏移量6为1、2或3

这至少会让你更精确一点


或者您可以捕获异常并继续,但我认为您需要一个挑战:)

您可以读取流的前几个字节,并将它们与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,仅前两个字节就可以了,更多信息)

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

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

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


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

我继续编写了一组函数来解决这个问题。它首先检查头部,然后尝试在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;
}

如果以后需要为其他操作和/或其他文件类型(例如PSD)读取数据,则使用
Image.FromStream
函数不一定是一个好主意。

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

这应该可以做到-您不必从标头中读取原始字节:

using(Image test = Image.FromFile(filePath))
{
    bool isJpeg = (test.RawFormat.Equals(ImageFormat.Jpeg));
}
当然,您也应该捕获OutOfMemoryException,如果文件根本不是图像,它将保存您

此外,ImageFormat还为GDI+支持的所有其他主要图像类型预先设置了项


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

我使用分号的答案并转换为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

这是我的图像检查。我不能依赖文件扩展名,必须自己检查格式。 我正在从字节数组中加载WPF中的位图图像,但事先不知道其格式。WPF可以很好地检测格式,但不会告诉您BitmapImage对象的图像格式(至少我不知道该对象的属性)。我不想用System.Drawing再次加载图像,只是为了检测格式。这个解决方案很快,对我来说效果很好

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

注意到上述所有函数都存在一些问题。 首先,-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
//
///检查是否有有效的图像文件格式。
/// 
/// 
///如果是图像文件,则为true
公共静态bool IsValidImageFormat(字符串路径)
{
使用(FileStream fs=File.OpenRead(path))
{
字节[]头=新字节[10];
fs.Read(头,0,10);
foreach(新字节中的var模式[][]{
Encoding.ASCII.GetBytes(“BM”),
Encoding.ASCII.GetBytes(“GIF”),
新字节[]{137,80,78,71},//PNG
新字节[]{73,73,42},//TIFF
新字节[]{77,77,42},//TIFF
新字节[]{255,216,255,224},//jpeg
新字节[]{255,216,255,225}//jpeg佳能
} )
{
if(pattern.SequenceEqual(header.Take(pattern.Length)))
返回true;
}
}
返回false;
}//IsValidImage格式
2019此处,d
public static bool IsImage(this byte[] fileBytes)
{
    var headers = new List<byte[]>
    {
        Encoding.ASCII.GetBytes("BM"),      // BMP
        Encoding.ASCII.GetBytes("GIF"),     // 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
    };

    return headers.Any(x => x.SequenceEqual(fileBytes.Take(x.Length)));
}
public async Task UploadImage(Stream file)
{
    using (MemoryStream ms = new MemoryStream())
    {
        await file.CopyToAsync(ms);

        byte[] bytes = ms.ToArray();

        if (!bytes.IsImage())
            throw new ArgumentException("Not an image", nameof(file));

        // Upload your file
    }
}
public class ImageValidator
{
    private readonly Dictionary<string,byte[]> _validBytes = new Dictionary<string, byte[]>() {
        { ".bmp", new byte[] { 66, 77 } },
        { ".gif", new byte[] { 71, 73, 70, 56 } },
        { ".ico", new byte[] { 0, 0, 1, 0 } },
        { ".jpg", new byte[] { 255, 216, 255 } },
        { ".png", new byte[] { 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82 } },
        { ".tiff", new byte[] { 73, 73, 42, 0 } },
    };

    /// <summary>
    /// image formats to validate using Guids from ImageFormat.
    /// </summary>
    private readonly Dictionary<Guid, string> _validGuids = new Dictionary<Guid, string>() {
        {ImageFormat.Jpeg.Guid, ".jpg" },
        {ImageFormat.Png.Guid, ".png"},
        {ImageFormat.Bmp.Guid, ".bmp"},
        {ImageFormat.Gif.Guid, ".gif"},
        {ImageFormat.Tiff.Guid, ".tiff"},
        {ImageFormat.Icon.Guid, ".ico" }
    };

    /// <summary>
    /// Supported extensions: .jpg,.png,.bmp,.gif,.tiff,.ico
    /// </summary>
    /// <param name="allowedExtensions"></param>
    public ImageValidator(string allowedExtensions = ".jpg;.png")
    {
        var exts = allowedExtensions.Split(';');
        foreach (var pair in _validGuids.ToArray())
        {
            if (!exts.Contains(pair.Value))
            {
                _validGuids.Remove(pair.Key);
            }
        }

        foreach (var pair in _validBytes.ToArray())
        {
            if (!exts.Contains(pair.Key))
            {
                _validBytes.Remove(pair.Key);
            }
        }
    }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0063:Use simple 'using' statement", Justification = "<Pending>")]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "<Pending>")]
    public async Task<bool> IsValidAsync(Stream imageStream, string filePath)
    {
        if(imageStream == null || imageStream.Length == 0)
        {
            return false;
        }

        //First validate using file extension
        string ext = Path.GetExtension(filePath).ToLower();
        if(!_validGuids.ContainsValue(ext))
        {
            return false;
        }

        //Check mimetype by content
        if(!await IsImageBySigAsync(imageStream, ext))
        {
            return false;
        }

        try
        {
            //Validate file using Guid.
            using (var image = Image.FromStream(imageStream))
            {
                imageStream.Position = 0;
                var imgGuid = image.RawFormat.Guid;
                if (!_validGuids.ContainsKey(imgGuid))
                {
                    return false;
                }

                var validExtension = _validGuids[imgGuid];
                if (validExtension != ext)
                {
                    return false;
                }
            }
        }
        catch (OutOfMemoryException)
        {
            return false;
        }

        return true;
    }

    /// <summary>
    /// Validate the mimetype using byte and file extension.
    /// </summary>
    /// <param name="imageStream"></param>
    /// <param name="extension"></param>
    /// <returns></returns>
    private async Task<bool> IsImageBySigAsync(Stream imageStream, string extension)
    {
        var length = _validBytes.Max(x => x.Value.Length);
        byte[] imgByte = new byte[length];
        await imageStream.ReadAsync(imgByte, 0, length);
        imageStream.Position = 0;

        if (_validBytes.ContainsKey(extension))
        {
            var validImgByte = _validBytes[extension];
            if (imgByte.Take(validImgByte.Length).SequenceEqual(validImgByte))
            {
                return true;
            }
        }

        return false;
    }
}