Nick Clarke
Nick Clarke

Reputation: 1282

Finding out the ContentType of a Image from the byte[]

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.

Upvotes: 9

Views: 20551

Answers (6)

Alexander Derck
Alexander Derck

Reputation: 14498

Rewrote the method of Nick Clarke a bit using Linq:

public class Program
{
   public static void Main(string[] args)
   {
      byte[] byteArray = File.ReadAllBytes(@"C:/users/Alexander/image.jpg");
      ImagePartType type = byteArray.GetImageType();
   }
}


public static class ImageHelper
{
    public static ImagePartType GetImageType(this byte[] imageBytes)
    {
        foreach(var imageType in imageFormatDecoders)
        {
            if (imageType.Key.SequenceEqual(imageBytes.Take(imageType.Key.Length)))
                return imageType.Value;
        }

        throw new ArgumentException("Imagetype is unknown!");
    }

    private static Dictionary<byte[], ImagePartType> imageFormatDecoders 
                     = new Dictionary<byte[], ImagePartType>()
    {
       { new byte[]{ 0x42, 0x4D }, ImagePartType.Bmp},
       { new byte[]{ 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, ImagePartType.Gif },
       { new byte[]{ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, ImagePartType.Gif },
       { new byte[]{ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, ImagePartType.Png },
       { new byte[]{ 0xff, 0xd8 }, ImagePartType.Jpeg }
    };
}

I used ImagePartType because I'm working with Open XML SDK at the moment, but just change the type in the dictionary :)

Upvotes: 4

Joost Aarts
Joost Aarts

Reputation: 683

This worked for me, ms being the memorystream. The downside is that it has to load the image.

Dim fmt As System.Drawing.Imaging.ImageFormat
Dim content As String

Using bmp As New Drawing.Bitmap(ms)
    fmt = bmp.RawFormat
End Using

Select Case fmt.Guid
    Case Drawing.Imaging.ImageFormat.Bmp.Guid
        content = "image/x-ms-bmp"

    Case Drawing.Imaging.ImageFormat.Jpeg.Guid
        content = "image/jpeg"

    Case Drawing.Imaging.ImageFormat.Gif.Guid
        content = "image/gif"

    Case Drawing.Imaging.ImageFormat.Png.Guid
        content = "image/png"

    Case Else
        content = "application/octet-stream"

End Select

Upvotes: 7

Nick Clarke
Nick Clarke

Reputation: 1282

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

Upvotes: 15

Darin Dimitrov
Darin Dimitrov

Reputation: 1038790

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.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500515

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);

Upvotes: 0

vit
vit

Reputation: 2685

Check out this file signatures table.

Upvotes: 16

Related Questions