9

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.

Nick Clarke
  • 1,262
  • 1
  • 10
  • 13

6 Answers6

16

Check out this file signatures table.

vit
  • 2,675
  • 2
  • 18
  • 15
15

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 },
    };
Community
  • 1
  • 1
Nick Clarke
  • 1,262
  • 1
  • 10
  • 13
  • Out of curiosity, is there a reason your loops increment with "i += 1" instead of "i++"? – Portman Aug 11 '09 at 20:20
  • 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 Aug 12 '09 at 09:16
  • @Portman, personal styles and preferences. Some people think `i++` is clearer, others think that it is less "best practice". See this: http://stackoverflow.com/questions/971312/why-avoid-increment-and-decrement-operators-in-javascript – ANeves Oct 25 '12 at 12:06
7

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
Joost Aarts
  • 673
  • 9
  • 19
4

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

Alexander Derck
  • 13,818
  • 5
  • 54
  • 76
1

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
  • 1,023,142
  • 271
  • 3,287
  • 2,928
0

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
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 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 Aug 07 '09 at 16:11
  • 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 Aug 07 '09 at 17:40