it-swarm.com.de

Bild aus Datei in C # validieren

Ich lade ein Bild aus einer Datei und möchte wissen, wie das Bild überprüft wird, bevor es vollständig aus der Datei gelesen wird.

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

Das Problem tritt auf, wenn image.jpg nicht wirklich ein jpg ist. Wenn ich beispielsweise eine leere Textdatei erstelle und in image.jpg umbenenne, wird beim Laden von image.jpg eine OutOfMemory-Ausnahme ausgelöst.

Ich suche nach einer Funktion, mit der ein Bild anhand eines Streams oder eines Dateipfads des Bildes überprüft werden kann.

Beispiel Funktionsprototyp

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

JPEGs haben keine formale Header-Definition, aber eine kleine Menge von Metadaten, die Sie verwenden können.

  • Offset 0 (zwei Bytes): JPEG SOI Marker (FFD8 hex)
  • Offset 2 (Zwei Bytes): Bildbreite in Pixel
  • Offset 4 (Zwei Bytes): Bildhöhe in Pixel
  • Offset 6 (Byte): Anzahl der Komponenten (1 = Graustufen, 3 = RGB)

Danach gibt es noch ein paar andere Dinge, die aber nicht wichtig sind.

Sie können die Datei mit einem Binärdatenstrom öffnen und diese Anfangsdaten lesen und sicherstellen, dass OffSet 0 0 und OffSet 6 entweder 1,2 oder 3 ist.

Das würde dir zumindest etwas mehr Präzision geben.

Oder Sie können einfach die Ausnahme abfangen und weitermachen, aber ich dachte, Sie wollten eine Herausforderung :)

22
FlySwat

hier ist mein Bildcheck. Ich kann mich nicht auf Dateierweiterungen verlassen und muss das Format selbst überprüfen. Ich lade BitmapImages in WPF aus Bytearrays und kenne das Format im Voraus nicht. WPF erkennt das Format in Ordnung, teilt Ihnen jedoch nicht das Bildformat von BitmapImage-Objekten mit (zumindest ist mir hierfür keine Eigenschaft bekannt). Und ich möchte das Bild nicht erneut mit System.Drawing laden, nur um das Format zu erkennen. Diese Lösung ist schnell und funktioniert gut für mich.

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

Verwenden von Windows Forms:

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

Andernfalls, wenn Sie mit WPF sind, können Sie Folgendes tun:

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

Sie müssen das erstellte Image freigeben. Andernfalls würde dies beim häufigen Aufrufen dieser Funktion OutOfMemoryException auslösen, da dem System die Ressourcen ausgehen, und nicht, weil das Bild beschädigt ist und ein falsches Ergebnis liefert, und wenn Sie Bilder nach diesem Schritt löschen , Sie würden möglicherweise gute löschen.

33
MusiGenesis

Nun, ich habe eine Reihe von Funktionen programmiert, um das Problem zu lösen. Es prüft zuerst den Header und versucht dann, das Bild in einen Try/Catch-Block zu laden. Es wird nur nach GIF-, BMP-, JPG- und PNG-Dateien gesucht. Sie können problemlos weitere Typen hinzufügen, indem Sie imageHeaders eine Kopfzeile hinzufügen.

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

Sie können eine grobe Eingabe vornehmen, indem Sie an der Kopfzeile schnüffeln.

Dies bedeutet, dass jedes von Ihnen implementierte Dateiformat einen identifizierbaren Header haben muss ...

JPEG: Die ersten 4 Bytes sind FF D8 FF E0 (eigentlich würden es nur die ersten zwei Bytes für non jfif jpeg tun, mehr Info hier ).

GIF: Die ersten 6 Bytes sind entweder "GIF87a" oder "GIF89a" (weitere Informationen hier )

PNG: Die ersten 8 Bytes sind: 89 50 4E 47 0D 0A 1A 0A (weitere Informationen hier )

TIFF: Die ersten 4 Bytes sind: II42 oder MM42 (weitere Informationen hier )

etc ... Sie können Header-/Formatinformationen für nahezu jedes Grafikformat finden, das Sie interessieren, und die Dinge, die es handhabt, nach Bedarf ergänzen. Was dies nicht tun wird, ist Ihnen mitzuteilen, ob die Datei eine gültige Version dieses Typs ist, aber es wird Ihnen ein Hinweis zu "Bild nicht Bild?" Es könnte sich immer noch um ein beschädigtes oder unvollständiges Image handeln und daher beim Öffnen abstürzen. Daher ist ein Versuch, den Aufruf von .FromFile zu umgehen, immer noch erforderlich.

12
Troy Howard

Dies sollte der Trick sein - Sie müssen keine unformatierten Bytes aus dem Header lesen:

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

Natürlich sollten Sie auch die OutOfMemoryException abfangen, die Sie speichert, wenn die Datei überhaupt kein Bild ist.

ImageFormat verfügt über voreingestellte Elemente für alle anderen wichtigen Bildtypen, die von GDI + unterstützt werden.

Beachten Sie, dass Sie für ImageFormat-Objekte .Equals () und nicht == verwenden müssen (dies ist keine Aufzählung), da der Operator == nicht überladen ist, um die Equals-Methode aufzurufen.

6
David Boike

Eine Methode, die auch Tiff und Jpeg unterstützt

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();
        }
    }
}
3
Paulo

Bemerkte ein paar Probleme mit allen oben genannten Funktionen. Zuallererst - Image.FromFile öffnet ein bestimmtes Bild und verursacht danach einen Fehler beim Öffnen einer Datei, wenn eine bestimmte Bilddatei aus irgendeinem Grund geöffnet werden soll. Sogar die Anwendung selbst - also habe ich mit Image.FromStream gewechselt.

Nachdem Sie eine API-Ausnahmebedingung gewechselt haben, wurde der Typ aus mir unklaren Gründen von OutOfMemoryException in ArgumentException geändert. (Wahrscheinlich .net Framework Bug?)

Auch wenn .net mehr Bilddateiformate als derzeit unterstützt, wird die Funktion überprüft. Es ist sinnvoll, zuerst zu versuchen, ein Bild zu laden, wenn dies nur dann fehlschlägt. Erst danach wird ein Fehler gemeldet.

Also mein Code sieht jetzt so aus:

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

Wo:

/// <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
2
TarmoPikaro

Ich nahm Semikolons Antwort und konvertierte zu 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
1
ray

wenn Sie diese Daten später für andere Operationen und/oder für andere Dateitypen (z. B. PSD) lesen möchten, verwenden Sie die Taste Image.FromStream Funktion ist nicht unbedingt eine gute Idee.

0
lorddarq

Ich würde eine Methode erstellen wie:

Image openImage(string filename);

in dem ich die ausnahme bearbeite. Wenn der zurückgegebene Wert Null ist, ist der Dateiname/-typ ungültig.

0
Enrico Murru

Sie können die ersten Bytes des Streams lesen und sie mit den magischen Header-Bytes für JPEG vergleichen.

0