Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 958
Nick Clarke Avatar asked Aug 07 '09 15:08

Nick Clarke


4 Answers

Check out this file signatures table.

like image 170
vit Avatar answered Nov 05 '22 21:11

vit


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 },
    };
like image 31
Nick Clarke Avatar answered Nov 05 '22 21:11

Nick Clarke


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
like image 38
Joost Aarts Avatar answered Nov 05 '22 22:11

Joost Aarts


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

like image 42
Alexander Derck Avatar answered Nov 05 '22 21:11

Alexander Derck