【问题标题】:Finding out the ContentType of a Image from the byte[]从 byte[] 中找出图像的 ContentType
【发布时间】:2010-11-17 18:00:00
【问题描述】:

有没有办法仅从原始字节中找出图像的 ContentType 是什么?

目前我有一个只存储字节[]的数据库列,我用它来在网页上显示图像。

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

我当然可以将 ContentType 保存在表格的另一列中,但只是想知道是否还有其他方法,例如也许 .Net 有办法查询数据以获取类型。

【问题讨论】:

    标签: c# asp.net asp.net-mvc


    【解决方案1】:

    看看这个file signatures table

    【讨论】:

    • 为链接干杯。我做了一些搜索,现在我知道要寻找什么,这确实是要走的路。
    • 根据您的想法,我在下面放置了代码的工作版本。
    【解决方案2】:

    文件/魔术签名是要走的路。以下是代码的工作版本。

    参考: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);
    

    然后是辅助类:

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

    【讨论】:

    • 出于好奇,您的循环是否以“i += 1”而不是“i++”递增?
    • 我从另一个 Stackoverflow 问题/答案(如上所述)中改编了代码。此后代码发生了变化,因为我对 i++ 的理解也比 i +=1 多。
    • @Portman,个人风格和偏好。有些人认为i++ 更清晰,其他人认为它不是“最佳实践”。看到这个:stackoverflow.com/questions/971312/…
    【解决方案3】:

    这对我有用,ms 是内存流。缺点是它必须加载图像。

    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
    

    【讨论】:

      【解决方案4】:

      用 Linq 稍微改写了 Nick Clarke 的方法:

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

      我使用了ImagePartType,因为我目前正在使用 Open XML SDK,但只需更改字典中的类型 :)

      【讨论】:

        【解决方案5】:

        没有标准的方法可以从内置的 .NET 流中检测内容类型。您可以实现自己的算法,通过读取前几个字节并尝试匹配格式,可以为某些 well-known 图像格式实现此目的。

        【讨论】:

          【解决方案6】:

          RawFormat 属性是否适合您?

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

          【讨论】:

          • RawFormat 似乎适用于 jpeg 和 gif,但不适用于 png。对于测试,我还尝试将其硬编码为 jpeg,并且效果相同(对于所有而不是 png),我猜 .Net 对 png 应用了更多逻辑。
          • 从我看到的其他示例中,RawFormat 似乎仅在从文件系统加载图像而不是通过字节(db 列)创建时才有效。
          猜你喜欢
          • 2015-12-26
          • 2017-07-06
          • 1970-01-01
          • 1970-01-01
          • 2019-03-15
          • 1970-01-01
          • 2013-05-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多