Подтвердить что ты не робот

Как определить, является ли файл файлом изображения в .NET?

Я не хочу полагаться на расширение файла. Я не хочу знать, какой тип изображения (.jpg,.png и т.д.), Я просто хочу знать, является ли файл изображением или нет. Я бы предпочел не использовать любые DLL файлы .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, если файл не является допустимым форматом изображения. Использование приведенного выше дает мне точно результат, который я хочу, однако я бы предпочел не использовать его по следующим причинам:

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

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

Изменить:. Получив ответы здесь, это три решения. Теперь я знаю:

  • Загрузите все изображение в память через Image.FromFile() и попытку.
    • Плюсы: более глубокая проверка содержимого содержимого файлов изображений; охватывает многие типы изображений.
    • Минусы: Slowest; накладные расходы от попытки захвата и загрузки полного файла изображения в память; потенциальная опасность от ловли "реального" исключения OutOfMemoryException.
  • Проверьте байты заголовка файла изображения.
    • Плюсы: быстрое использование с низкой памятью.
    • Минусы: потенциально хрупкие; необходимо запрограммировать для каждого типа файла.
  • Проверьте расширение файла.
    • Плюсы: быстрый; Простейшие.
    • Минусы: не работает во всех ситуациях; наиболее легко ошибочно.

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

4b9b3361

Ответ 1

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

Ответ 2

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

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

  • Файлы изображений 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).

Ответ 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,
    }

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

Ответ 4

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

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

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

Ответ 5

здесь, где используются сигнатуры в 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;
}

Ответ 6

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