views:

243

answers:

4

Is there a way to find out what the ContentType of an image is from only the original bytes?

At the moment I have a database column that stores only the byte[], which I use to display an image on a web page.

MemoryStream ms = new MemoryStream(imageBytes);
Image image = Image.FromStream(ms);
image.Save(context.HttpContext.Response.OutputStream, <--ContentType-->);

I could of course just save the ContentType in another column in the table, but just wondered if there was another way e.g. maybe .Net has a way to interrogate the data to get the type.

+8  A: 

Check out this file signatures table.

vit
+1 Nice answer.
Dead account
Cheers for the link. I did some searching, now I know what to look for and this does appear the way to go.
Nick Clarke
I have put a working version of the code below, based on your idea.
Nick Clarke
A: 

Does the RawFormat property work for you?

MemoryStream ms = new MemoryStream(imageBytes);
Image image = Image.FromStream(ms);
image.Save(context.HttpContext.Response.OutputStream, image.RawFormat);
Jon Skeet
RawFormat seems to work for jpeg and gif but not png. For a test I also tried hardcoding it to jpeg and this worked the same (for all and not png), I guess .Net applies more logic to png.
Nick Clarke
From other examples I have seen RawFormat appears to only work when the image was loaded from the filesystem and not created via bytes (db column).
Nick Clarke
+1  A: 

There's no standard way to detect content type from a stream built-in .NET. You could implement your own algorithm that could achieve this for some well-known image formats by reading the first few bytes and trying to match the format.

Darin Dimitrov
+2  A: 

File/magic signatures was the way to go. Below is the working version of the code.

Ref: Stackoverflow - Getting image dimensions without reading the entire file

ImageFormat contentType = ImageHelper.GetContentType(this.imageBytes);

MemoryStream ms = new MemoryStream(this.imageBytes);
Image image = Image.FromStream(ms);
image.Save(context.HttpContext.Response.OutputStream, contentType);

And then the helper class:

public static class ImageHelper
{
    public static ImageFormat GetContentType(byte[] imageBytes)
    {
        MemoryStream ms = new MemoryStream(imageBytes);

        using (BinaryReader br = new BinaryReader(ms))
        {
            int maxMagicBytesLength = imageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length;

            byte[] magicBytes = new byte[maxMagicBytesLength];

            for (int i = 0; i < maxMagicBytesLength; i += 1)
            {
                magicBytes[i] = br.ReadByte();

                foreach (var kvPair in imageFormatDecoders)
                {
                    if (magicBytes.StartsWith(kvPair.Key))
                    {
                        return kvPair.Value;
                    }
                }
            }

            throw new ArgumentException("Could not recognise image format", "binaryReader");
        }
    }

    private static bool StartsWith(this byte[] thisBytes, byte[] thatBytes)
    {
        for (int i = 0; i < thatBytes.Length; i += 1)
        {
            if (thisBytes[i] != thatBytes[i])
            {
                return false;
            }
        }
        return true;
    }

    private static Dictionary<byte[], ImageFormat> imageFormatDecoders = new Dictionary<byte[], ImageFormat>()
    {
        { new byte[]{ 0x42, 0x4D }, ImageFormat.Bmp},
        { new byte[]{ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, ImageFormat.Gif },
        { new byte[]{ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, ImageFormat.Gif },
        { new byte[]{ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, ImageFormat.Png },
        { new byte[]{ 0xff, 0xd8 }, ImageFormat.Jpeg },
    };
Nick Clarke
Out of curiosity, is there a reason your loops increment with "i += 1" instead of "i++"?
Portman
I adapted the code from another Stackoverflow question/answer (mentioned above). The code has since changed as I also understand i++ more than i +=1.
Nick Clarke