2012-02-20 1 views
15

Я не хочу полагаться на расширение файла. Я не хочу знать, какой тип изображения (.jpg, .png и т. Д.), Я просто хочу знать, является ли файл изображением или нет. Я бы предпочел не использовать DLL-файлы не .NET, если это возможно.Как определить, является ли файл файлом изображения в .NET?

Лучший способом я знаю, как сделать это следующим образом:

bool isImageFile; 
try 
{ 
    Image.FromFile(imageFile).Dispose(); 
    isImageFile = true; 
} 
catch (OutOfMemoryException) 
{ 
    isImageFile = false; 
} 

Как отмечался здесь: http://msdn.microsoft.com/en-us/library/stf701f5.aspx, Image.FromFile() бросает OutOfMemoryException, если файл не является допустимым формат изображения. Использование выше дает мне именно тот результат, я хочу, однако я предпочел бы не использовать его по следующим причинам:

  • Это мое убеждение в том, что с помощью Try-уловов для нормального выполнения программы является плохой практикой по соображениям производительности.
  • Image.FromFile() загружает весь файл изображения (если это файл изображения) в память. Это расточительно, я предполагаю, потому что мне нужен только тип файла и не нужно делать никаких дальнейших манипуляций с изображением на этом этапе моего кода.
  • Мне не нравится ловить OutOfMemoryException, потому что, если есть РЕАЛЬНАЯ проблема с памятью, и моя программа проглатывает ее и продолжает идти?

Есть ли лучшие способы сделать это? Или, все или все перечисленные выше проблемы необоснованны?

Edit: После получения ответов здесь, эти три решения теперь я в курсе:

  1. Загрузите все изображения в памяти через Image.FromFile() и примерку улов.
    • Профи: Проводит более глубокую проверку содержимого содержимого изображений; охватывает многие типы изображений.
    • Против: Slowest; накладные расходы от попытки захвата и загрузки полного файла изображения в память; потенциальная опасность от ловли «реального» исключения OutOfMemoryException.
  2. Проверьте байты заголовка файла изображения.
    • Достоинства: Быстрый, низкий объем памяти.
    • Против: потенциально хрупкий; необходимо запрограммировать для каждого типа файла.
  3. Проверьте расширение файла.
    • Прос: Самый быстрый; Простейшие.
    • Против: Не работает во всех ситуациях; наиболее легко неправильно.

(я не вижу четкий «победителя», так как я могу представить себе ситуацию, в которой каждый из них будет уместно. Для целей моего приложения, проверка типа файла происходит достаточно редко, что касается производительности метод 1 не является проблемой.)

+0

Смотрите также: http://stackoverflow.com/questions/670546/determine-if-file-is-an-image – Daryl

+0

Смотрите также # 2 : http://stackoverflow.com/questions/210650/validate-image-from-file-in-c-sharp – Daryl

+0

См. также # 3: http://stackoverflow.com/questions/55869/determine-file-type- of-an-image – Daryl

ответ

7
  1. Вы заметите только падение производительности из исключений, если вы постоянно бросать их. Поэтому, если ваша программа не увидит много недопустимых изображений (сотни в секунду), вы не должны замечать накладные расходы на обработку исключений.
  2. Это действительно единственный способ узнать, является ли изображение полным изображением или повреждено. Вы можете проверить заголовки, как рекомендуют другие люди, но это только проверяет, правильно ли начато несколько байтов, что-то еще может быть мусором. Является ли это достаточно хорошим или нет, зависит от требований вашего приложения. Просто чтение заголовка может быть достаточно хорошим для вашего случая использования.
  3. Да, это довольно плохой дизайн на стороне команды BCL. Если вы загружаете много больших изображений, вы вполне можете столкнуться с реальной ситуацией OOM в большой кучке объектов. Насколько я знаю, нет возможности различать два исключения.
+0

Спасибо за информацию. Я решил для своей ситуации, что удар производительности не был значительным. – Daryl

10

Если вы будете поддерживать только несколько популярных графических форматов, то вы можете просто прочитать первые несколько байт файла, чтобы определить тип, основанный на Magic Number

Примеры из приведенной ссылки:

  • файлы GIF изображения имеют код ASCII для "GIF89a" (47 49 46 38 39 61) или "GIF87a" (47 49 46 38 37 61)
  • JPEG файлов изображений начинаются с FF D8 и заканчиваться FF D9. Файлы JPEG/JFIF содержат код ASCII для «JFIF» (4A 46 49 46) в виде строки с нулевым завершением.
  • Файлы изображений PNG начинаются с 8-байтной сигнатуры, которая идентифицирует файл как файл PNG и позволяет обнаруживать общие проблемы передачи файлов: \ 211 PNG \ r \ n \ 032 \ n (89 50 4E 47 0D 0A 1A 0A).
+1

Спасибо за информацию; Я не знал, что это можно сделать. Я написал реализацию и разместил ее здесь как вики. – Daryl

+0

Хорошее сообщение ......... – Ansari

1

Сначала используйте метод System.IO.Path.GetExtension(), чтобы убедиться, что расширение является типом изображения. Затем, если вы хотите пройти через, вы можете проверить заголовки в файле.

+0

Что делать, если нет расширения? – Coding4Fun

2

Я могу понять ваши проблемы, но если вы посмотрите на источник метода Image.FromFile, это просто оболочка для вызовов GDI +, так что, к сожалению, вы ничего не можете сделать, так как я вижу, что выбор исключения bizzare (OutOfMemoryException) было сделано в GDI +

Так что кажется, что вы застряли в текущем коде или проверяете заголовки файлов, но это не гарантирует, что файл действительно является допустимым изображением.

Возможно, вам стоит подумать, действительно ли вам нужен метод isImageFile? Обнаруживать файлы изображений на расширении, это будет намного быстрее, и если загрузка из файла не удастся, он вызовет исключение, чтобы вы могли справиться с ним, когда вам действительно нужно загрузить изображение.

3

Принимая маршрут проверки заголовка файла, я написал эту реализацию:

public static ImageType GetFileImageTypeFromHeader(string file) 
    { 
     byte[] headerBytes; 
     using (FileStream fileStream = new FileStream(file, FileMode.Open)) 
     { 
      const int mostBytesNeeded = 11;//For JPEG 

      if (fileStream.Length < mostBytesNeeded) 
       return ImageType.Unknown; 

      headerBytes = new byte[mostBytesNeeded]; 
      fileStream.Read(headerBytes, 0, mostBytesNeeded); 
     } 

     //Sources: 
     //http://stackoverflow.com/questions/9354747 
     //http://en.wikipedia.org/wiki/Magic_number_%28programming%29#Magic_numbers_in_files 
     //http://www.mikekunz.com/image_file_header.html 

     //JPEG: 
     if (headerBytes[0] == 0xFF &&//FF D8 
      headerBytes[1] == 0xD8 && 
      (
      (headerBytes[6] == 0x4A &&//'JFIF' 
       headerBytes[7] == 0x46 && 
       headerBytes[8] == 0x49 && 
       headerBytes[9] == 0x46) 
       || 
      (headerBytes[6] == 0x45 &&//'EXIF' 
       headerBytes[7] == 0x78 && 
       headerBytes[8] == 0x69 && 
       headerBytes[9] == 0x66) 
      ) && 
      headerBytes[10] == 00) 
     { 
      return ImageType.JPEG; 
     } 
     //PNG 
     if (headerBytes[0] == 0x89 && //89 50 4E 47 0D 0A 1A 0A 
      headerBytes[1] == 0x50 && 
      headerBytes[2] == 0x4E && 
      headerBytes[3] == 0x47 && 
      headerBytes[4] == 0x0D && 
      headerBytes[5] == 0x0A && 
      headerBytes[6] == 0x1A && 
      headerBytes[7] == 0x0A) 
     { 
      return ImageType.PNG; 
     } 
     //GIF 
     if (headerBytes[0] == 0x47 &&//'GIF' 
      headerBytes[1] == 0x49 && 
      headerBytes[2] == 0x46) 
     { 
      return ImageType.GIF; 
     } 
     //BMP 
     if (headerBytes[0] == 0x42 &&//42 4D 
      headerBytes[1] == 0x4D) 
     { 
      return ImageType.BMP; 
     } 
     //TIFF 
     if ((headerBytes[0] == 0x49 &&//49 49 2A 00 
      headerBytes[1] == 0x49 && 
      headerBytes[2] == 0x2A && 
      headerBytes[3] == 0x00) 
      || 
      (headerBytes[0] == 0x4D &&//4D 4D 00 2A 
      headerBytes[1] == 0x4D && 
      headerBytes[2] == 0x00 && 
      headerBytes[3] == 0x2A)) 
     { 
      return ImageType.TIFF; 
     } 

     return ImageType.Unknown; 
    } 
    public enum ImageType 
    { 
     Unknown, 
     JPEG, 
     PNG, 
     GIF, 
     BMP, 
     TIFF, 
    } 

я положил это в грузопассажирских/вспомогательный класс вдоль наряду с методами: GetFileImageTypeFromFullLoad() и GetFileImageTypeFromExtension().Первый использует мой вышеупомянутый подход Image.FromFile, и последний просто проверяет расширение файла. Я планирую использовать все три в зависимости от требований ситуации.

+0

Вот еще один, с которым я столкнулся. Это в основном то же самое, но немного меньше: – Justin

+0

@Panuvin, вы забыли включить ссылку? – Daryl

2

вот тот, который использует подписи в GDI +:

public static ImageCodecInfo DetectCodec(Stream stream) 
{ 
    var ib = 0; 

    var rgCodecs = ImageCodecInfo.GetImageDecoders(); 
    for (var cCodecs = rgCodecs.Length; cCodecs > 0;) 
    { 
     var b = stream.ReadByte(); 
     if (b == -1) 
      return null; // EOF 

     for (int iCodec = cCodecs - 1; iCodec >= 0; iCodec--) 
     { 
      var codec = rgCodecs[iCodec]; 
      for (int iSig = 0; iSig < codec.SignaturePatterns.Length; iSig++) 
      { 
       var mask = codec.SignatureMasks[iSig]; 
       var patt = codec.SignaturePatterns[iSig]; 

       if (ib >= patt.Length) 
        return codec; 

       if ((b & mask[ib]) != patt[ib]) 
       { 
        rgCodecs[iCodec] = rgCodecs[--cCodecs]; 
        break; 
       } 
      } 
     } 

     ib++; 
    } 

    return null; 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^