[ACCEPTED]-How can I determine if a file is an image file in .NET?-file-type

Accepted answer
Score: 11

If you will only be supporting a handful 4 of the popular image formats, then you can 3 simply read the first few bytes of the file 2 to determine the type based on the Magic Number

Examples 1 from the link provided:

  • GIF image files have the ASCII code for "GIF89a" (47 49 46 38 39 61) or "GIF87a" (47 49 46 38 37 61)
  • JPEG image files begin with FF D8 and end with FF D9. JPEG/JFIF files contain the ASCII code for "JFIF" (4A 46 49 46) as a null terminated string.
  • PNG image files begin with an 8-byte signature which identifies the file as a PNG file and allows detection of common file transfer problems: \211 P N G \r \n \032 \n (89 50 4E 47 0D 0A 1A 0A).
Score: 8
  1. You will only notice a performance hit from exceptions if you are constantly throwing them. So unless your program expects to see many invalid images (hundreds per second) you should not notice the overhead of exception handling.
  2. This is really the only way to tell if the image is a full image or corrupt. You can check the headers as the other people recommend, but that only checks to see if the beginning few bytes are correct, anything else could be garbage. Whether this is good enough or not depends on the requirements of your application. Just reading the header might be good enough for your use case.
  3. Yes, this is rather poor design on the BCL team's part. If you are loading many large images you very well could be hitting a real OOM situation in the large object heap. As far as I know, there is no way to differentiate the two exceptions.

0

Score: 3

I can understand your concerns, but if you 14 look at the source of Image.FromFile method, it's 13 just wrapper for GDI+ calls, so sadly you 12 can't do nothing, since as I can see that 11 bizzare choice of exception (OutOfMemoryException) was 10 done in GDI+

So it seems that you are stuck 9 with current code, or check file headers, but 8 that will not guarantee that file is really 7 an valid image.

Maybe you should consider 6 do you really need isImageFile method in 5 the first place ? Detect image files on 4 extension, it would be much faster and if 3 loading from file fails it will raise an 2 exception so you can deal with it when you 1 really need to load image.

Score: 3

Taking the route of checking the file header, I 7 wrote this implementation:

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

I put this into 6 a utility/helper class along along with 5 methods: GetFileImageTypeFromFullLoad() and GetFileImageTypeFromExtension(). The former uses my above 4 mentioned Image.FromFile approach and the latter simply 3 checks the file extension. I plan to use 2 all three depending on the requirements 1 of the situation.

Score: 1

Use the System.IO.Path.GetExtension() method 3 first to check if the Extension is an image 2 type. Then if you want to be through you 1 can check the headers in the file.

Score: 1

here's one that uses the signatures in 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;
}

0

More Related questions