2008-10-16 2 views
44

Я загружаю изображение из файла, и я хочу знать, как проверить изображение до его полного чтения из файла.Подтвердить изображение из файла в C#

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

Проблема возникает, когда изображение.jpg на самом деле не является jpg. Например, если я создаю пустой текстовый файл и переименую его в image.jpg, при загрузке image.jpg будет выбрано исключение OutOfMemory.

Я ищу функцию, которая будет проверять изображение с учетом потока или пути к файлу изображения.

Пример прототип функции

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

Почему бы не обернуть этот код в Try ... Catch блок, и если он бросает это исключение, вы можете считать его «недействительным»? Конечно, это наивная эвристика, но она выполняет эту работу. Все остальное должно будет открыть файл, поэтому вы не будете экономить значительную часть производительности независимо от ИМО. – 2008-10-16 23:41:24

+0

См. Также: http://stackoverflow.com/questions/9354747/how-can-i-determine-if-a-file-is-an-image-file-in-net – Daryl 2013-03-05 21:41:22

ответ

21

JPEG не имеют формального определения заголовка, но у них есть небольшое количество метаданных, которые вы можете использовать.

  • Смещение 0 (два байта): JPEG КНИ маркер (FFD8 гекс)
  • Смещение 2 (два байта): Ширина изображения в пикселях
  • Offset 4 (два байта): Высота изображения в пикселях
  • Offset 6 (байт): Количество компонентов (1 = градаций серого, 3 = RGB)

есть несколько других вещей, после этого, но это не важно.

Вы можете открыть файл, используя двоичный поток, и читать эти исходные данные, и убедитесь, что OffSet 0 = 0, и OffSet 6 либо 1,2 или 3.

Это было бы по крайней мере дать вам немного больше точности.

Или вы можете просто перехватить исключение и двигаться дальше, но я думал, что ты хотел вызов :)

+0

Я бы пошел дальше и прочитал заголовок для файла и сравнил с массивом файлов заголовков файлов с поддержкой .NET. В конце концов, я буду кодировать это и опубликовать его как решение для тех, кому это понадобится в будущем. – SemiColon 2008-10-17 00:48:46

+1

Простое чтение заголовков не гарантирует, что файл действителен и не будет генерировать исключение при открытии в Image.FromFile(). – MusiGenesis 2008-10-17 14:19:23

1

Я хотел бы создать метод, как:

Image openImage(string filename); 

, в котором я обрабатывать исключение. Если возвращаемое значение равно Null, существует недопустимое имя/тип файла.

+0

LOL, я, должно быть, писал это как комментарий, когда вы разместили это. Я согласен с этим ответом, это достаточно просто, чтобы выполнить эту работу. – 2008-10-16 23:42:08

+0

Этот метод является просто неправильным. Вы не должны контролировать поток программы с использованием исключений. Также .. Исключения, возвращенные с этого конкретного вызова, могут быть * очень * ошибочными и неоднозначными. – 2009-10-23 01:02:53

+0

Я не вижу, что в этом плохого. Человек, который написал openImage, выбрал исключение, если изображение недействительно, вместо предоставления возвращаемого значения. Поэтому мне кажется, что ловить и обрабатывать исключение - это то, как они предназначены для вас, чтобы справиться с этой ситуацией. – pilavdzice 2012-04-25 17:43:32

0

Вы можете прочитать первые несколько байт потока и сравнить их с волшебными байтов заголовка для JPEG.

30

Использование 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; 
} 

В противном случае, если вы с помощью 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, потому что в системе закончились ресурсы, а не потому, что изображение повреждено, что приводит к некорректному результату, и если вы удалите изображения после этого шага, удалять хорошие.

11

Вы можете сделать грубую типизацию, обнюхивая заголовок.

Это означает, что каждый файл формат вы реализуете должно иметь идентификационный заголовок ...

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.

18

Ну, я пошел вперед и закодировал набор функций для решения проблемы. Сначала он проверяет заголовок, затем пытается загрузить изображение в блок 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; 
} 
0

в случае лет нужно, что считывание данных для других операций и/или для других типов файлов (PSD), например, в дальнейшем, а затем с помощью функции Image.FromStream не обязательно является хорошим ideea.

3

Метод, который поддерживает 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(); 
     } 
    } 
} 
6

Это должно сделать трюк - вы не должны читать необработанные байты из заголовка:

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

Конечно, вы должен также захватить OutOfMemoryException, который сохранит вас, если файл не является изображением вообще.

И, ImageFormat имеет предустановленные элементы для всех других основных типов изображений, поддерживаемых GDI +.

Обратите внимание: вы должны использовать .Equals() и not == для объектов ImageFormat (это не перечисление), потому что оператор == не перегружен для вызова метода Equals.

1

Я принял ответ 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 
51

вот мой чек изображение. Я не могу полагаться на расширения файлов и должен сам проверять формат. Я загружаю BitmapImages в 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; 
} 
0

Заметили проблему со всеми вышеперечисленными функциями. Прежде всего - изображение.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 
Смежные вопросы