我如何知道从流中获得的图像格式是什么?

发布于 2024-10-20 16:02:58 字数 457 浏览 3 评论 0原文

我从某些网络服务获取字节流。该字节流包含图像的二进制数据,我使用下面 C# 中的方法将其转换为 Image 实例。

我需要知道我有什么样的形象。它是简单的位图 (*.bmp) 还是 JPEG 图像 (*.jpg) 或 png 图像?

我怎样才能找到它?

    public static Image byteArrayToImage( byte[] bmpBytes )
    {
        Image image = null;
        using( MemoryStream stream = new MemoryStream( bmpBytes ) )
        {
            image = Image.FromStream( stream );
        }

        return image;
    }

I get a byte stream from some web service. This byte stream contains the binary data of an image and I'm using the method in C# below to convert it to an Image instance.

I need to know what kind of image I've got. Is it a simple bitmap (*.bmp) or a JPEG image (*.jpg) or a png image?

How can I find it out?

    public static Image byteArrayToImage( byte[] bmpBytes )
    {
        Image image = null;
        using( MemoryStream stream = new MemoryStream( bmpBytes ) )
        {
            image = Image.FromStream( stream );
        }

        return image;
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

黒涩兲箜 2024-10-27 16:02:58

您可以查看 Image.RawFormat 属性。因此,一旦您从流中加载图像,您就可以测试:

if (ImageFormat.Jpeg.Equals(image.RawFormat))
{
    // JPEG
}
else if (ImageFormat.Png.Equals(image.RawFormat))
{
    // PNG
}
else if (ImageFormat.Gif.Equals(image.RawFormat))
{
    // GIF
}
... etc

You may checkout the Image.RawFormat property. So once you load the image from the stream you could test:

if (ImageFormat.Jpeg.Equals(image.RawFormat))
{
    // JPEG
}
else if (ImageFormat.Png.Equals(image.RawFormat))
{
    // PNG
}
else if (ImageFormat.Gif.Equals(image.RawFormat))
{
    // GIF
}
... etc
贪恋 2024-10-27 16:02:58

您可以通过以下代码获取图像类型:

//get your image from bytaArrayToImage
Image img = byteArrayToImage(new byte[] { });

//get the format/file type
string ext = new ImageFormatConverter().ConvertToString(img.RawFormat);

You can get the image type from the following code:

//get your image from bytaArrayToImage
Image img = byteArrayToImage(new byte[] { });

//get the format/file type
string ext = new ImageFormatConverter().ConvertToString(img.RawFormat);
土豪我们做朋友吧 2024-10-27 16:02:58

在C#中,我们可以使用 Image.RawFormat检查图像的图像格式。但属性 RawFormat 似乎在紧凑框架中不可用。所以我认为你必须使用 Imaging API。需要的类型有很多。下面我给你举一个例子,我想这会对你有所帮助。

using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;

/// <summary>
/// This structure contains parameters related to an image.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ImageInfo
{
    #region Fields

    /// <summary>
    /// A GUID value that identifies the file format for the native image data. This value is an image format identifier. For more information, see Imaging GUIDs.
    /// </summary>
    public Guid RawDataFormat;

    public PixelFormatID PixelFormat;

    public uint Width;

    public uint Height;

    public uint TileWidth;

    public uint TileHeight;

    public double Xdpi;

    public double Ydpi;

    public SinkFlags Flags;

    #endregion
}

[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
    #region Fields

    public int dwLowDateTime;

    public int dwHighDateTime;

    #endregion
}

[StructLayout(LayoutKind.Sequential)]
internal struct BitmapData
{
    #region Fields

    public int Width;

    public int Height;

    public int Stride;

    public PixelFormatID PixelFormat;

    public IntPtr Scan0;

    public IntPtr Reserved;

    #endregion
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STATSTG
{
    #region Fields

    [MarshalAs(UnmanagedType.LPWStr)]
    public string pwcsName;

    public int type;

    public long cbSize;

    public FILETIME mtime;

    public FILETIME ctime;

    public FILETIME atime;

    public int grfMode;

    public int grfLocksSupported;

    public Guid clsid;

    public int grfStateBits;

    public int reserved;

    #endregion
}

/// <summary>
/// COM IStream interface
/// </summary>
[ComImport]
[Guid("0000000c-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IStream
{
    void Read([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbRead);

    void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbWritten);

    void Seek(long dlibMove, int origin, IntPtr plibNewPosition);

    void SetSize(long libNewSize);

    void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten);

    void Commit(int grfCommitFlags);

    void Revert();

    void LockRegion(long libOffset, long cb, int lockType);

    void UnlockRegion(long libOffset, long cb, int lockType);

    void Stat(out STATSTG pstatstg, int grfStatFlag);

    void Clone(out IStream ppstm);
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDAA-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
internal interface IBitmapImage
{
    uint GetSize(out Size size);

    uint GetPixelFormatID(out PixelFormatID pixelFormat);

    uint LockBits(ref RECT rect, uint flags, PixelFormatID pixelFormat, ref BitmapData lockedBitmapData);

    uint UnlockBits(ref BitmapData lockedBitmapData);

    uint GetPalette(); // This is a place holder

    uint SetPalette(); // This is a place holder
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDA7-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
internal interface IImagingFactory
{
    uint CreateImageFromStream(IStream stream, out IImage image);

    uint CreateImageFromFile(string filename, out IImage image);

    // We need the MarshalAs attribute here to keep COM interop from sending the buffer down as a Safe Array.
    // uint CreateImageFromBuffer([MarshalAs(UnmanagedType.LPArray)] byte[] buffer, uint size, BufferDisposalFlag disposalFlag, out IImage image);
    uint CreateImageFromBuffer(IntPtr buffer, uint size, BufferDisposalFlag disposalFlag, out IImage image);

    uint CreateNewBitmap();            // This is a place holder

    uint CreateBitmapFromImage(IImage image, uint width, uint height, PixelFormatID pixelFormat, InterpolationHint hints, out IBitmapImage bitmap);      // This is a place holder

    uint CreateBitmapFromBuffer();     // This is a place holder

    uint CreateImageDecoder();         // This is a place holder

    uint CreateImageEncoderToStream(); // This is a place holder

    uint CreateImageEncoderToFile(ref Guid clsid, string filename, out IImageEncoder encoder);

    uint GetInstalledDecoders([Out] out uint size, [Out] out IntPtr decoders);

    uint GetInstalledEncoders([Out] out uint size, [Out] out IntPtr ecoders);

    uint InstallImageCodec();          // This is a place holder

    uint UninstallImageCodec();        // This is a place holder
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDAC-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IImageEncoder
{
    int InitEncoder(IStream stream);

    int TerminateEncoder();

    int GetEncodeSink();

    int SetFrameDimension(ref Guid dimensionID);

    int GetEncoderParameterListSize(out uint size);

    int GetEncoderParameterList(uint size, out IntPtr @params);

    int SetEncoderParameters(IntPtr param);
}

/// <summary>
/// This structure defines the coordinates of the upper-left and lower-right corners of a rectangle. 
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
    #region Fields

    public int left;

    public int top;

    public int right;

    public int bottom;

    #endregion
}

// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
internal enum BufferDisposalFlag : int
{
    BufferDisposalFlagNone,
    BufferDisposalFlagGlobalFree,
    BufferDisposalFlagCoTaskMemFree,
    BufferDisposalFlagUnmapView
}

// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
internal enum InterpolationHint : int
{
    InterpolationHintDefault,
    InterpolationHintNearestNeighbor,
    InterpolationHintBilinear,
    InterpolationHintAveraging,
    InterpolationHintBicubic
}

/// <summary>
/// These values are flags used to identify the numerical formats of pixels in images.
/// </summary>
[Flags]
public enum PixelFormatID
{
    PixelFormatIndexed = 0x00010000,

    PixelFormatGDI = 0x00020000,

    PixelFormatAlpha = 0x00040000,

    PixelFormatPAlpha = 0x00080000,

    PixelFormatExtended = 0x00100000,

    PixelFormatCanonical = 0x00200000,

    PixelFormatUndefined = 0,

    PixelFormat1bppIndexed = (1 | (1 << 8) | PixelFormatIndexed | PixelFormatGDI),

    PixelFormat4bppIndexed = (2 | (4 << 8) | PixelFormatIndexed | PixelFormatGDI),

    PixelFormat16bppGrayScale = (4 | (16 << 8) | PixelFormatExtended),

    PixelFormat8bppIndexed = (3 | (8 << 8) | PixelFormatIndexed | PixelFormatGDI),

    PixelFormat16bppRGB555 = (5 | (16 << 8) | PixelFormatGDI),

    PixelFormat16bppRGB565 = (6 | (16 << 8) | PixelFormatGDI),

    PixelFormat16bppARGB1555 = (7 | (16 << 8) | PixelFormatAlpha | PixelFormatGDI),

    PixelFormat24bppRGB = (8 | (24 << 8) | PixelFormatGDI),

    PixelFormat32bppRGB = (9 | (32 << 8) | PixelFormatGDI),

    PixelFormat32bppARGB = (10 | (32 << 8) | PixelFormatAlpha | PixelFormatGDI | PixelFormatCanonical),

    PixelFormat32bppPARGB = (11 | (32 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatGDI),

    PixelFormat48bppRGB = (12 | (48 << 8) | PixelFormatExtended),

    PixelFormat64bppARGB = (13 | (64 << 8) | PixelFormatAlpha | PixelFormatCanonical | PixelFormatExtended),

    PixelFormat64bppPARGB = (14 | (64 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatExtended),

    PixelFormatMax = 15
}

[Flags]
internal enum SinkFlags : uint
{
    // Low-word: shared with ImgFlagx
    Scalable = 0x0001,
    HasAlpha = 0x0002,
    HasTranslucent = 0x0004,
    PartiallyScalable = 0x0008,

    ColorSpaceRGB = 0x0010,
    ColorSpaceCMYK = 0x0020,
    ColorSpaceGRAY = 0x0040,
    ColorSpaceYCBCR = 0x0080,
    ColorSpaceYCCK = 0x0100,

    // Low-word: image size info
    HasRealDPI = 0x1000,
    HasRealPixelSize = 0x2000,

    // High-word
    TopDown = 0x00010000,
    BottomUp = 0x00020000,
    FullWidth = 0x00040000,
    Multipass = 0x00080000,
    Composite = 0x00100000,
    WantProps = 0x00200000
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDA9-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
internal interface IImage
{
    uint GetPhysicalDimension(out Size size);

    uint GetImageInfo(ref ImageInfo info);

    uint SetImageFlags(uint flags);

    // "Correct" declaration: uint Draw(IntPtr hdc, ref Rectangle dstRect, ref Rectangle srcRect);
    uint Draw(IntPtr hdc, ref RECT dstRect, IntPtr srcRect);

    uint PushIntoSink(); // This is a place holder

    uint GetThumbnail(uint thumbWidth, uint thumbHeight, out IImage thumbImage);
}

/// <summary>
/// Implmentation for the COM IStream interface
/// </summary>
public sealed class StreamOnFile :
    IStream,
    IDisposable
{
    #region Fields

    private readonly Stream stream;
    private readonly string fileName;

    #endregion

    #region Constructors

    /// <summary>
    ///     Initializes a new instance of the <see cref="StreamOnFile"/> class.
    /// </summary>
    /// <param name="fileName">File name to open
    /// </param>
    internal StreamOnFile(string fileName)
    {
        this.fileName = fileName;

        // prevent another processes/threads (mainly delete from web) from using this file while Imaging API is trying to access it
        this.stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets the file name
    /// </summary>
    public string FileName
    {
        get
        {
            return this.fileName;
        }
    }

    #endregion

    #region Methods

    public void Read(byte[] pv, int cb, IntPtr pcbRead)
    {
        int val = this.stream.Read(pv, 0, cb);
        if (pcbRead != IntPtr.Zero)
        {
            Marshal.WriteInt32(pcbRead, val);
        }
    }

    public void Write(byte[] pv, int cb, IntPtr pcbWritten)
    {
        this.stream.Write(pv, 0, cb);
        if (pcbWritten != IntPtr.Zero)
        {
            Marshal.WriteInt32(pcbWritten, cb);
        }
    }

    public void Seek(long dlibMove, int origin, IntPtr plibNewPosition)
    {
        long val = this.stream.Seek(dlibMove, (SeekOrigin)origin);
        if (plibNewPosition != IntPtr.Zero)
        {
            Marshal.WriteInt64(plibNewPosition, val);
        }
    }

    public void SetSize(long libNewSize)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Commit(int grfCommitFlags)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Revert()
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void LockRegion(long libOffset, long cb, int lockType)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void UnlockRegion(long libOffset, long cb, int lockType)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Stat(out STATSTG pstatstg, int grfStatFlag)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Clone(out IStream ppstm)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Dispose()
    {
        if (this.stream != null)
        {
            this.stream.Close();
        }
    }

    #endregion
}

public enum ImageType
{
    Undefined,
    MemoryBMP,
    BMP,
    EMF,
    WMF,
    JPEG,
    PNG,
    GIF,
    TIFF,
    EXIF,
    Icon
}

public static class BitmapProperties
{
    /// <summary>
    /// Indicates the Microsoft Windowsbitmap (BMP) format.
    /// </summary>
    private static readonly Guid ImageFormatBMP = new Guid("B96B3CAB-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Enhanced Metafile (EMF) format.
    /// </summary>
    private static readonly Guid ImageFormatEMF = new Guid("B96B3CAC-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Exif (Exchangeable Image File) format.
    /// </summary>
    private static readonly Guid ImageFormatEXIF = new Guid("B96B3CB2-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Graphics Interchange Format (GIF) format.
    /// </summary>
    private static readonly Guid ImageFormatGIF = new Guid("B96B3CB0-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Icon format.
    /// </summary>
    private static readonly Guid ImageFormatIcon = new Guid("B96B3CB5-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the JPEG format.
    /// </summary>
    private static readonly Guid ImageFormatJPEG = new Guid("B96B3CAE-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates that the image was constructed from a memory bitmap.
    /// </summary>
    private static readonly Guid ImageFormatMemoryBMP = new Guid("B96B3CAB-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Portable Network Graphics (PNG) format.
    /// </summary>
    private static readonly Guid ImageFormatPNG = new Guid("B96B3CAF-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Tagged Image File Format (TIFF) format.
    /// </summary>
    private static readonly Guid ImageFormatTIFF = new Guid("B96B3CB1-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates that Windows GDI+ is unable to determine the format.
    /// </summary>
    private static readonly Guid ImageFormatUndefined = new Guid("B96B3CA9-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Windows Metafile Format (WMF) format.
    /// </summary>
    private static readonly Guid ImageFormatWMF = new Guid("B96B3CAD-0728-11D3-9D7B-0000F81EF32E");

    internal const int S_OK = 0;

    /// <summary>
    /// Gets the ImageType of the given file
    /// </summary>
    /// <param name="fileName">Path of the file to get the info of</param>
    /// <returns>ImageType of the given file</returns>
    public static ImageType GetImageType(string fileName)
    {
        IImage imagingImage = null;

        try
        {
            if (File.Exists(fileName))
            {
                using (StreamOnFile fileStream = new StreamOnFile(fileName))
                {
                    imagingImage = BitmapProperties.GetImage(fileStream);
                    if (imagingImage != null)
                    {
                        ImageInfo info = new ImageInfo();
                        uint ret = imagingImage.GetImageInfo(ref info);
                        if (ret == BitmapProperties.S_OK)
                        {
                            if (info.RawDataFormat == BitmapProperties.ImageFormatBMP)
                            {
                                return ImageType.BMP;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatEMF)
                            {
                                return ImageType.EMF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatEXIF)
                            {
                                return ImageType.EXIF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatGIF)
                            {
                                return ImageType.GIF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatIcon)
                            {
                                return ImageType.Icon;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatJPEG)
                            {
                                return ImageType.JPEG;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatMemoryBMP)
                            {
                                return ImageType.MemoryBMP;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatPNG)
                            {
                                return ImageType.PNG;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatTIFF)
                            {
                                return ImageType.TIFF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatWMF)
                            {
                                return ImageType.WMF;
                            }
                        }
                    }
                }
            }

            return ImageType.Undefined;
        }
        finally
        {
            if (imagingImage != null)
            {
                Marshal.ReleaseComObject(imagingImage);
            }
        }
    }

    private static IImage GetImage(StreamOnFile stream)
    {
        IImagingFactory factory = null;
        try
        {
            factory = (IImagingFactory)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("327ABDA8-072B-11D3-9D7B-0000F81EF32E")));
            if (factory != null)
            {
                IImage imagingImage;
                uint result = factory.CreateImageFromStream(stream, out imagingImage);
                if (result == BitmapProperties.S_OK)
                {
                    return imagingImage;
                }
            }
        }
        catch (COMException)
        {
        }
        catch (IOException)
        {
        }
        finally
        {
            if (factory != null)
            {
                Marshal.ReleaseComObject(factory);
            }
        }

        return null;
    }
}

In C#, we can use Image.RawFormat to check the ImageFormat of an Image. But property RawFormat seems not available in compact Framework. So I think you have to use the Imaging API. There are a lot of types needed. Here in the bellow I am giving you an example I think it will help you some what.

using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;

/// <summary>
/// This structure contains parameters related to an image.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct ImageInfo
{
    #region Fields

    /// <summary>
    /// A GUID value that identifies the file format for the native image data. This value is an image format identifier. For more information, see Imaging GUIDs.
    /// </summary>
    public Guid RawDataFormat;

    public PixelFormatID PixelFormat;

    public uint Width;

    public uint Height;

    public uint TileWidth;

    public uint TileHeight;

    public double Xdpi;

    public double Ydpi;

    public SinkFlags Flags;

    #endregion
}

[StructLayout(LayoutKind.Sequential)]
public struct FILETIME
{
    #region Fields

    public int dwLowDateTime;

    public int dwHighDateTime;

    #endregion
}

[StructLayout(LayoutKind.Sequential)]
internal struct BitmapData
{
    #region Fields

    public int Width;

    public int Height;

    public int Stride;

    public PixelFormatID PixelFormat;

    public IntPtr Scan0;

    public IntPtr Reserved;

    #endregion
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STATSTG
{
    #region Fields

    [MarshalAs(UnmanagedType.LPWStr)]
    public string pwcsName;

    public int type;

    public long cbSize;

    public FILETIME mtime;

    public FILETIME ctime;

    public FILETIME atime;

    public int grfMode;

    public int grfLocksSupported;

    public Guid clsid;

    public int grfStateBits;

    public int reserved;

    #endregion
}

/// <summary>
/// COM IStream interface
/// </summary>
[ComImport]
[Guid("0000000c-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IStream
{
    void Read([Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbRead);

    void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, IntPtr pcbWritten);

    void Seek(long dlibMove, int origin, IntPtr plibNewPosition);

    void SetSize(long libNewSize);

    void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten);

    void Commit(int grfCommitFlags);

    void Revert();

    void LockRegion(long libOffset, long cb, int lockType);

    void UnlockRegion(long libOffset, long cb, int lockType);

    void Stat(out STATSTG pstatstg, int grfStatFlag);

    void Clone(out IStream ppstm);
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDAA-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
internal interface IBitmapImage
{
    uint GetSize(out Size size);

    uint GetPixelFormatID(out PixelFormatID pixelFormat);

    uint LockBits(ref RECT rect, uint flags, PixelFormatID pixelFormat, ref BitmapData lockedBitmapData);

    uint UnlockBits(ref BitmapData lockedBitmapData);

    uint GetPalette(); // This is a place holder

    uint SetPalette(); // This is a place holder
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDA7-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
internal interface IImagingFactory
{
    uint CreateImageFromStream(IStream stream, out IImage image);

    uint CreateImageFromFile(string filename, out IImage image);

    // We need the MarshalAs attribute here to keep COM interop from sending the buffer down as a Safe Array.
    // uint CreateImageFromBuffer([MarshalAs(UnmanagedType.LPArray)] byte[] buffer, uint size, BufferDisposalFlag disposalFlag, out IImage image);
    uint CreateImageFromBuffer(IntPtr buffer, uint size, BufferDisposalFlag disposalFlag, out IImage image);

    uint CreateNewBitmap();            // This is a place holder

    uint CreateBitmapFromImage(IImage image, uint width, uint height, PixelFormatID pixelFormat, InterpolationHint hints, out IBitmapImage bitmap);      // This is a place holder

    uint CreateBitmapFromBuffer();     // This is a place holder

    uint CreateImageDecoder();         // This is a place holder

    uint CreateImageEncoderToStream(); // This is a place holder

    uint CreateImageEncoderToFile(ref Guid clsid, string filename, out IImageEncoder encoder);

    uint GetInstalledDecoders([Out] out uint size, [Out] out IntPtr decoders);

    uint GetInstalledEncoders([Out] out uint size, [Out] out IntPtr ecoders);

    uint InstallImageCodec();          // This is a place holder

    uint UninstallImageCodec();        // This is a place holder
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDAC-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IImageEncoder
{
    int InitEncoder(IStream stream);

    int TerminateEncoder();

    int GetEncodeSink();

    int SetFrameDimension(ref Guid dimensionID);

    int GetEncoderParameterListSize(out uint size);

    int GetEncoderParameterList(uint size, out IntPtr @params);

    int SetEncoderParameters(IntPtr param);
}

/// <summary>
/// This structure defines the coordinates of the upper-left and lower-right corners of a rectangle. 
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
    #region Fields

    public int left;

    public int top;

    public int right;

    public int bottom;

    #endregion
}

// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
internal enum BufferDisposalFlag : int
{
    BufferDisposalFlagNone,
    BufferDisposalFlagGlobalFree,
    BufferDisposalFlagCoTaskMemFree,
    BufferDisposalFlagUnmapView
}

// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
internal enum InterpolationHint : int
{
    InterpolationHintDefault,
    InterpolationHintNearestNeighbor,
    InterpolationHintBilinear,
    InterpolationHintAveraging,
    InterpolationHintBicubic
}

/// <summary>
/// These values are flags used to identify the numerical formats of pixels in images.
/// </summary>
[Flags]
public enum PixelFormatID
{
    PixelFormatIndexed = 0x00010000,

    PixelFormatGDI = 0x00020000,

    PixelFormatAlpha = 0x00040000,

    PixelFormatPAlpha = 0x00080000,

    PixelFormatExtended = 0x00100000,

    PixelFormatCanonical = 0x00200000,

    PixelFormatUndefined = 0,

    PixelFormat1bppIndexed = (1 | (1 << 8) | PixelFormatIndexed | PixelFormatGDI),

    PixelFormat4bppIndexed = (2 | (4 << 8) | PixelFormatIndexed | PixelFormatGDI),

    PixelFormat16bppGrayScale = (4 | (16 << 8) | PixelFormatExtended),

    PixelFormat8bppIndexed = (3 | (8 << 8) | PixelFormatIndexed | PixelFormatGDI),

    PixelFormat16bppRGB555 = (5 | (16 << 8) | PixelFormatGDI),

    PixelFormat16bppRGB565 = (6 | (16 << 8) | PixelFormatGDI),

    PixelFormat16bppARGB1555 = (7 | (16 << 8) | PixelFormatAlpha | PixelFormatGDI),

    PixelFormat24bppRGB = (8 | (24 << 8) | PixelFormatGDI),

    PixelFormat32bppRGB = (9 | (32 << 8) | PixelFormatGDI),

    PixelFormat32bppARGB = (10 | (32 << 8) | PixelFormatAlpha | PixelFormatGDI | PixelFormatCanonical),

    PixelFormat32bppPARGB = (11 | (32 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatGDI),

    PixelFormat48bppRGB = (12 | (48 << 8) | PixelFormatExtended),

    PixelFormat64bppARGB = (13 | (64 << 8) | PixelFormatAlpha | PixelFormatCanonical | PixelFormatExtended),

    PixelFormat64bppPARGB = (14 | (64 << 8) | PixelFormatAlpha | PixelFormatPAlpha | PixelFormatExtended),

    PixelFormatMax = 15
}

[Flags]
internal enum SinkFlags : uint
{
    // Low-word: shared with ImgFlagx
    Scalable = 0x0001,
    HasAlpha = 0x0002,
    HasTranslucent = 0x0004,
    PartiallyScalable = 0x0008,

    ColorSpaceRGB = 0x0010,
    ColorSpaceCMYK = 0x0020,
    ColorSpaceGRAY = 0x0040,
    ColorSpaceYCBCR = 0x0080,
    ColorSpaceYCCK = 0x0100,

    // Low-word: image size info
    HasRealDPI = 0x1000,
    HasRealPixelSize = 0x2000,

    // High-word
    TopDown = 0x00010000,
    BottomUp = 0x00020000,
    FullWidth = 0x00040000,
    Multipass = 0x00080000,
    Composite = 0x00100000,
    WantProps = 0x00200000
}

/// <summary>
/// Pulled from imaging.h in the Windows Mobile 5.0 Pocket PC SDK
/// </summary>
[ComImport]
[Guid("327ABDA9-072B-11D3-9D7B-0000F81EF32E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
internal interface IImage
{
    uint GetPhysicalDimension(out Size size);

    uint GetImageInfo(ref ImageInfo info);

    uint SetImageFlags(uint flags);

    // "Correct" declaration: uint Draw(IntPtr hdc, ref Rectangle dstRect, ref Rectangle srcRect);
    uint Draw(IntPtr hdc, ref RECT dstRect, IntPtr srcRect);

    uint PushIntoSink(); // This is a place holder

    uint GetThumbnail(uint thumbWidth, uint thumbHeight, out IImage thumbImage);
}

/// <summary>
/// Implmentation for the COM IStream interface
/// </summary>
public sealed class StreamOnFile :
    IStream,
    IDisposable
{
    #region Fields

    private readonly Stream stream;
    private readonly string fileName;

    #endregion

    #region Constructors

    /// <summary>
    ///     Initializes a new instance of the <see cref="StreamOnFile"/> class.
    /// </summary>
    /// <param name="fileName">File name to open
    /// </param>
    internal StreamOnFile(string fileName)
    {
        this.fileName = fileName;

        // prevent another processes/threads (mainly delete from web) from using this file while Imaging API is trying to access it
        this.stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets the file name
    /// </summary>
    public string FileName
    {
        get
        {
            return this.fileName;
        }
    }

    #endregion

    #region Methods

    public void Read(byte[] pv, int cb, IntPtr pcbRead)
    {
        int val = this.stream.Read(pv, 0, cb);
        if (pcbRead != IntPtr.Zero)
        {
            Marshal.WriteInt32(pcbRead, val);
        }
    }

    public void Write(byte[] pv, int cb, IntPtr pcbWritten)
    {
        this.stream.Write(pv, 0, cb);
        if (pcbWritten != IntPtr.Zero)
        {
            Marshal.WriteInt32(pcbWritten, cb);
        }
    }

    public void Seek(long dlibMove, int origin, IntPtr plibNewPosition)
    {
        long val = this.stream.Seek(dlibMove, (SeekOrigin)origin);
        if (plibNewPosition != IntPtr.Zero)
        {
            Marshal.WriteInt64(plibNewPosition, val);
        }
    }

    public void SetSize(long libNewSize)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Commit(int grfCommitFlags)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Revert()
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void LockRegion(long libOffset, long cb, int lockType)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void UnlockRegion(long libOffset, long cb, int lockType)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Stat(out STATSTG pstatstg, int grfStatFlag)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Clone(out IStream ppstm)
    {
        throw new NotImplementedException("The method or operation is not implemented.");
    }

    public void Dispose()
    {
        if (this.stream != null)
        {
            this.stream.Close();
        }
    }

    #endregion
}

public enum ImageType
{
    Undefined,
    MemoryBMP,
    BMP,
    EMF,
    WMF,
    JPEG,
    PNG,
    GIF,
    TIFF,
    EXIF,
    Icon
}

public static class BitmapProperties
{
    /// <summary>
    /// Indicates the Microsoft Windowsbitmap (BMP) format.
    /// </summary>
    private static readonly Guid ImageFormatBMP = new Guid("B96B3CAB-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Enhanced Metafile (EMF) format.
    /// </summary>
    private static readonly Guid ImageFormatEMF = new Guid("B96B3CAC-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Exif (Exchangeable Image File) format.
    /// </summary>
    private static readonly Guid ImageFormatEXIF = new Guid("B96B3CB2-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Graphics Interchange Format (GIF) format.
    /// </summary>
    private static readonly Guid ImageFormatGIF = new Guid("B96B3CB0-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Icon format.
    /// </summary>
    private static readonly Guid ImageFormatIcon = new Guid("B96B3CB5-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the JPEG format.
    /// </summary>
    private static readonly Guid ImageFormatJPEG = new Guid("B96B3CAE-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates that the image was constructed from a memory bitmap.
    /// </summary>
    private static readonly Guid ImageFormatMemoryBMP = new Guid("B96B3CAB-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Portable Network Graphics (PNG) format.
    /// </summary>
    private static readonly Guid ImageFormatPNG = new Guid("B96B3CAF-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Tagged Image File Format (TIFF) format.
    /// </summary>
    private static readonly Guid ImageFormatTIFF = new Guid("B96B3CB1-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates that Windows GDI+ is unable to determine the format.
    /// </summary>
    private static readonly Guid ImageFormatUndefined = new Guid("B96B3CA9-0728-11D3-9D7B-0000F81EF32E");

    /// <summary>
    /// Indicates the Windows Metafile Format (WMF) format.
    /// </summary>
    private static readonly Guid ImageFormatWMF = new Guid("B96B3CAD-0728-11D3-9D7B-0000F81EF32E");

    internal const int S_OK = 0;

    /// <summary>
    /// Gets the ImageType of the given file
    /// </summary>
    /// <param name="fileName">Path of the file to get the info of</param>
    /// <returns>ImageType of the given file</returns>
    public static ImageType GetImageType(string fileName)
    {
        IImage imagingImage = null;

        try
        {
            if (File.Exists(fileName))
            {
                using (StreamOnFile fileStream = new StreamOnFile(fileName))
                {
                    imagingImage = BitmapProperties.GetImage(fileStream);
                    if (imagingImage != null)
                    {
                        ImageInfo info = new ImageInfo();
                        uint ret = imagingImage.GetImageInfo(ref info);
                        if (ret == BitmapProperties.S_OK)
                        {
                            if (info.RawDataFormat == BitmapProperties.ImageFormatBMP)
                            {
                                return ImageType.BMP;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatEMF)
                            {
                                return ImageType.EMF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatEXIF)
                            {
                                return ImageType.EXIF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatGIF)
                            {
                                return ImageType.GIF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatIcon)
                            {
                                return ImageType.Icon;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatJPEG)
                            {
                                return ImageType.JPEG;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatMemoryBMP)
                            {
                                return ImageType.MemoryBMP;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatPNG)
                            {
                                return ImageType.PNG;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatTIFF)
                            {
                                return ImageType.TIFF;
                            }
                            else if (info.RawDataFormat == BitmapProperties.ImageFormatWMF)
                            {
                                return ImageType.WMF;
                            }
                        }
                    }
                }
            }

            return ImageType.Undefined;
        }
        finally
        {
            if (imagingImage != null)
            {
                Marshal.ReleaseComObject(imagingImage);
            }
        }
    }

    private static IImage GetImage(StreamOnFile stream)
    {
        IImagingFactory factory = null;
        try
        {
            factory = (IImagingFactory)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("327ABDA8-072B-11D3-9D7B-0000F81EF32E")));
            if (factory != null)
            {
                IImage imagingImage;
                uint result = factory.CreateImageFromStream(stream, out imagingImage);
                if (result == BitmapProperties.S_OK)
                {
                    return imagingImage;
                }
            }
        }
        catch (COMException)
        {
        }
        catch (IOException)
        {
        }
        finally
        {
            if (factory != null)
            {
                Marshal.ReleaseComObject(factory);
            }
        }

        return null;
    }
}
神经大条 2024-10-27 16:02:58

这是我根据 Alex 提到的方法和参考文章编写的一个非常轻量级的 ImageDetector 类:

namespace ImageDetection
{
    using System;
    using System.Collections.Generic;
    using System.Drawing.Imaging;
    using System.Linq;

    // based on https://devblogs.microsoft.com/scripting/psimaging-part-1-test-image/
    public class ImageDetector
    {
        // https://en.wikipedia.org/wiki/List_of_file_signatures
        /* Bytes in c# have a range of 0 to 255 so each byte can be represented as
         * a two digit hex string. */
        private static readonly Dictionary<ImageFormat, string[][]> SignatureTable = new Dictionary<ImageFormat, string[][]>
        {
            {
                ImageFormat.Jpeg,
                new[]
                {
                    new[] {"FF", "D8", "FF", "DB"},
                    new[] {"FF", "D8", "FF", "EE"},
                    new[] {"FF", "D8", "FF", "E0", "00", "10", "4A", "46", "49", "46", "00", "01"}
                }
            },
            {
                ImageFormat.Gif,
                new[]
                {
                    new [] { "47", "49", "46", "38", "37", "61" },
                    new [] { "47", "49", "46", "38", "39", "61" }
                }
            },
            {
                ImageFormat.Png,
                new[]
                {
                    new[] {"89", "50", "4E", "47", "0D", "0A", "1A", "0A"}
                }
            },
            {
                ImageFormat.Bmp,
                new []
                {
                    new[] { "42", "4D" }
                }
            }
        };
        
        /// <summary>
        /// Takes a byte array and determines the image file type by
        /// comparing the first few bytes of the file to a list of known
        /// image file signatures.
        /// </summary>
        /// <param name="imageData">Byte array of the image data</param>
        /// <returns>ImageFormat corresponding to the image file format</returns>
        /// <exception cref="ArgumentException">Thrown if the image type can't be determined</exception>
        public static ImageFormat GetImageType(byte[] imageData)
        {
            foreach (KeyValuePair<ImageFormat, string[][]> signatureEntry in SignatureTable)
            {
                foreach (string[] signature in signatureEntry.Value)
                {
                    bool isMatch = true;
                    for (int i = 0; i < signature.Length; i++)
                    {
                        string signatureByte = signature[i];
                    
                        // ToString("X") gets the hex representation and pads it to always be length 2
                        string imageByte = imageData[i]
                            .ToString("X2");

                        if (signatureByte == imageByte)
                            continue;
                        isMatch = false;
                        break;
                    }

                    if (isMatch)
                    {
                        return signatureEntry.Key;
                    }
                }
            }
            
            throw new ArgumentException("The byte array did not match any known image file signatures.");
        }
    }
    
    
}

Here is a very lightweight ImageDetector class I wrote based on the approach Alex mentions and based on a referenced article:

namespace ImageDetection
{
    using System;
    using System.Collections.Generic;
    using System.Drawing.Imaging;
    using System.Linq;

    // based on https://devblogs.microsoft.com/scripting/psimaging-part-1-test-image/
    public class ImageDetector
    {
        // https://en.wikipedia.org/wiki/List_of_file_signatures
        /* Bytes in c# have a range of 0 to 255 so each byte can be represented as
         * a two digit hex string. */
        private static readonly Dictionary<ImageFormat, string[][]> SignatureTable = new Dictionary<ImageFormat, string[][]>
        {
            {
                ImageFormat.Jpeg,
                new[]
                {
                    new[] {"FF", "D8", "FF", "DB"},
                    new[] {"FF", "D8", "FF", "EE"},
                    new[] {"FF", "D8", "FF", "E0", "00", "10", "4A", "46", "49", "46", "00", "01"}
                }
            },
            {
                ImageFormat.Gif,
                new[]
                {
                    new [] { "47", "49", "46", "38", "37", "61" },
                    new [] { "47", "49", "46", "38", "39", "61" }
                }
            },
            {
                ImageFormat.Png,
                new[]
                {
                    new[] {"89", "50", "4E", "47", "0D", "0A", "1A", "0A"}
                }
            },
            {
                ImageFormat.Bmp,
                new []
                {
                    new[] { "42", "4D" }
                }
            }
        };
        
        /// <summary>
        /// Takes a byte array and determines the image file type by
        /// comparing the first few bytes of the file to a list of known
        /// image file signatures.
        /// </summary>
        /// <param name="imageData">Byte array of the image data</param>
        /// <returns>ImageFormat corresponding to the image file format</returns>
        /// <exception cref="ArgumentException">Thrown if the image type can't be determined</exception>
        public static ImageFormat GetImageType(byte[] imageData)
        {
            foreach (KeyValuePair<ImageFormat, string[][]> signatureEntry in SignatureTable)
            {
                foreach (string[] signature in signatureEntry.Value)
                {
                    bool isMatch = true;
                    for (int i = 0; i < signature.Length; i++)
                    {
                        string signatureByte = signature[i];
                    
                        // ToString("X") gets the hex representation and pads it to always be length 2
                        string imageByte = imageData[i]
                            .ToString("X2");

                        if (signatureByte == imageByte)
                            continue;
                        isMatch = false;
                        break;
                    }

                    if (isMatch)
                    {
                        return signatureEntry.Key;
                    }
                }
            }
            
            throw new ArgumentException("The byte array did not match any known image file signatures.");
        }
    }
    
    
}
李不 2024-10-27 16:02:58

我需要一个简单的 Java 图像格式检测器(用于 Android 应用程序),因此改编了 @Jared_Beach 答案,也添加了 WebP 格式检测:

import com.sun.istack.internal.Nullable;

// based on https://devblogs.microsoft.com/scripting/psimaging-part-1-test-image/
public class ImageDetector
{
    final static String[] imgFormats = {"jpg", "gif", "png", "bmp", "webp"};

    private enum ImageFormat {
        Jpeg (new int[][]{
                {0xFF, 0xD8, 0xFF, 0xDB},
                {0xFF, 0xD8, 0xFF, 0xEE},
                {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01}
        }),
        Gif (new int[][]{
                {0x47, 0x49, 0x46, 0x38, 0x37, 0x61},
                {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}
        }),
        Png (new int[][]{
                {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}
        }),
        Bmp (new int[][]{
                {0x42, 0x4D}
        }),
        WebP (new int[][]{
                // Ignores zeroes - image size goes there
                { 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'}
        });

        final byte[][] _bts;

        ImageFormat(int[][] b) {
            _bts = new byte[b.length][];
            for (int i = 0; i < b.length; i++) {
                _bts[i] = new byte[b[i].length];
                for (int j = 0; j < b[i].length; j++)
                    _bts[i][j] = (byte) b[i][j];
            }
        }
    }

    /**
     Takes a byte array and determines the image file type by
     comparing the first few bytes of the file to a list of known
     image file signatures.

     @param imageData Byte array of the image data
     @return type from imgFormats, it can be a MIME too type with "image/"+imgFormats[i], or simply file name extension,
             or null if type not recognized.
     */
    @Nullable
    public static String getImageType(byte[] imageData)
    {
        if (imageData.length > 16) { // too small, avoid index out of bounds exception
            for (ImageFormat imgF : ImageFormat.values()) {
                for (byte[] sig : imgF._bts) {
                    boolean isMatch = true;
                    for (int i = 0; i < sig.length; i++) {
                        if ((sig[i] != 0 || imgF != ImageFormat.WebP) && sig[i] != imageData[i]) {
                            isMatch = false;
                            break;
                        }
                    }
                    if (isMatch)
                        return imgFormats[imgF.ordinal()];
                }
            }
        }
        return null;
    }
}

这是一个小测试函数:

static void testImageDetector() {
    final String[] fileNames = {
            "C:/tmp/test.jpg",
            "C:/tmp/test.gif",
            "C:/tmp/test.png",
            "C:/tmp/test.bmp",
            "C:/tmp/test.webp",
    };
    FileInputStream fis = null;
    try {
        byte[] buf = new byte[64];
        String type;
        for (String fName : fileNames) {
            fis = new FileInputStream(fName);
            fis.read(buf);
            fis.close(); fis = null;
            String ext = fName.substring(fName.lastIndexOf('.') + 1);
            type = ImageDetector.getImageType(buf);
            System.out.println("File: " + fName + ", type: " + type);
            if (!ext.equals(type))
                throw new AssertionError("Wrong type detected!");
        }
    }
    catch (IOException iox) {
        System.out.println("IOException: " + iox);
        iox.printStackTrace();
    }
    catch (AssertionError ae) {
        ae.printStackTrace();
    }
    finally {
        if (fis != null) try {
            fis.close();
        } catch (IOException ignore) {}
    }
}

I needed a simple image format detector in Java (for an Android app), so adapted @Jared_Beach answer, added WebP format detection too:

import com.sun.istack.internal.Nullable;

// based on https://devblogs.microsoft.com/scripting/psimaging-part-1-test-image/
public class ImageDetector
{
    final static String[] imgFormats = {"jpg", "gif", "png", "bmp", "webp"};

    private enum ImageFormat {
        Jpeg (new int[][]{
                {0xFF, 0xD8, 0xFF, 0xDB},
                {0xFF, 0xD8, 0xFF, 0xEE},
                {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01}
        }),
        Gif (new int[][]{
                {0x47, 0x49, 0x46, 0x38, 0x37, 0x61},
                {0x47, 0x49, 0x46, 0x38, 0x39, 0x61}
        }),
        Png (new int[][]{
                {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}
        }),
        Bmp (new int[][]{
                {0x42, 0x4D}
        }),
        WebP (new int[][]{
                // Ignores zeroes - image size goes there
                { 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P'}
        });

        final byte[][] _bts;

        ImageFormat(int[][] b) {
            _bts = new byte[b.length][];
            for (int i = 0; i < b.length; i++) {
                _bts[i] = new byte[b[i].length];
                for (int j = 0; j < b[i].length; j++)
                    _bts[i][j] = (byte) b[i][j];
            }
        }
    }

    /**
     Takes a byte array and determines the image file type by
     comparing the first few bytes of the file to a list of known
     image file signatures.

     @param imageData Byte array of the image data
     @return type from imgFormats, it can be a MIME too type with "image/"+imgFormats[i], or simply file name extension,
             or null if type not recognized.
     */
    @Nullable
    public static String getImageType(byte[] imageData)
    {
        if (imageData.length > 16) { // too small, avoid index out of bounds exception
            for (ImageFormat imgF : ImageFormat.values()) {
                for (byte[] sig : imgF._bts) {
                    boolean isMatch = true;
                    for (int i = 0; i < sig.length; i++) {
                        if ((sig[i] != 0 || imgF != ImageFormat.WebP) && sig[i] != imageData[i]) {
                            isMatch = false;
                            break;
                        }
                    }
                    if (isMatch)
                        return imgFormats[imgF.ordinal()];
                }
            }
        }
        return null;
    }
}

and here is a small test function:

static void testImageDetector() {
    final String[] fileNames = {
            "C:/tmp/test.jpg",
            "C:/tmp/test.gif",
            "C:/tmp/test.png",
            "C:/tmp/test.bmp",
            "C:/tmp/test.webp",
    };
    FileInputStream fis = null;
    try {
        byte[] buf = new byte[64];
        String type;
        for (String fName : fileNames) {
            fis = new FileInputStream(fName);
            fis.read(buf);
            fis.close(); fis = null;
            String ext = fName.substring(fName.lastIndexOf('.') + 1);
            type = ImageDetector.getImageType(buf);
            System.out.println("File: " + fName + ", type: " + type);
            if (!ext.equals(type))
                throw new AssertionError("Wrong type detected!");
        }
    }
    catch (IOException iox) {
        System.out.println("IOException: " + iox);
        iox.printStackTrace();
    }
    catch (AssertionError ae) {
        ae.printStackTrace();
    }
    finally {
        if (fis != null) try {
            fis.close();
        } catch (IOException ignore) {}
    }
}
秋日私语 2024-10-27 16:02:58

将字节数组解析为实际的图像对象可能会资源密集型,尤其是对于大量数据

您可以使用幻数

JPEG 图像文件以 FF D8 开头...

......

PNG 图像文件以 8 字节签名开头,用于标识
文件为 PNG 文件并允许检测常见文件传输
问题:(89 50 4E 47 0D 0A 1A 0A)

等。

一些示例代码:

public bool IsPng(byte[] array)
{
    return array != null
        && array.Length > 8
        && array[0] == 0x89
        && array[1] == 0x50
        && array[2] == 0x4e
        && array[3] == 0x47
        && array[4] == 0x0d
        && array[5] == 0x0a
        && array[6] == 0x1a
        && array[7] == 0x0a
}

public static bool IsJpeg(byte[] array)
{
    return array != null
        && array.Length > 2
        && array[0] == 0xff
        && array[1] == 0xd8;
}

当然,您应该在快速检查后进一步验证数据,以 100% 确定格式是安全的并且是实际图像。

PS 这就是浏览器的做法

Parsing a byte array into an actual image object can be resource intensive especially for huge amounts of data

You can use Magic Numbers instead

JPEG image files begin with FF D8...

...

PNG image files begin with an 8-byte signature which identifies the
file as a PNG file and allows detection of common file transfer
problems: (89 50 4E 47 0D 0A 1A 0A)

etc.

Some example code for you:

public bool IsPng(byte[] array)
{
    return array != null
        && array.Length > 8
        && array[0] == 0x89
        && array[1] == 0x50
        && array[2] == 0x4e
        && array[3] == 0x47
        && array[4] == 0x0d
        && array[5] == 0x0a
        && array[6] == 0x1a
        && array[7] == 0x0a
}

public static bool IsJpeg(byte[] array)
{
    return array != null
        && array.Length > 2
        && array[0] == 0xff
        && array[1] == 0xd8;
}

Of course, you should further validate the data after this quick check to be 100% sure the format is safe and is an actual image.

P.S. this is how browsers do it

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文