在C#中,如何从byte[]得知文件类型?

问题描述 投票:0回答:12

我有一个从上传的文件填充的字节数组。但是,在代码的另一部分,我需要知道从 byte[] 上传的文件类型,以便我可以向浏览器呈现正确的内容类型!

谢谢!!

asp.net-mvc c#-3.0 arrays content-type
12个回答
25
投票

如前所述,MIME 魔法是实现此目的的唯一方法。许多平台提供了最新且强大的 MIME 魔术文件和代码来高效地完成此操作。在 .NET 中执行此操作而无需任何第 3 方代码的唯一方法是使用 urlmon.dll 中的

FindMimeFromData
。方法如下:

public static int MimeSampleSize = 256;

public static string DefaultMimeType = "application/octet-stream";

[DllImport(@"urlmon.dll", CharSet = CharSet.Auto)]
private extern static uint FindMimeFromData(
    uint pBC,
    [MarshalAs(UnmanagedType.LPStr)] string pwzUrl,
    [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer,
    uint cbSize,
    [MarshalAs(UnmanagedType.LPStr)] string pwzMimeProposed,
    uint dwMimeFlags,
    out uint ppwzMimeOut,
    uint dwReserverd
);

public static string GetMimeFromBytes(byte[] data) {
    try {
        uint mimeType;
        FindMimeFromData(0, null, data, (uint)MimeSampleSize, null, 0, out mimeType, 0);

        var mimePointer = new IntPtr(mimeType);
        var mime = Marshal.PtrToStringUni(mimePointer);
        Marshal.FreeCoTaskMem(mimePointer);

        return mime ?? DefaultMimeType;
    }
    catch {
        return DefaultMimeType;
    }
}

这使用 Internet Explorer MIME 检测器。这与 IE 用来发送 MIME 类型和上传文件的代码相同。您可以看到 urlmon.dll 支持的 MIME 类型列表。需要注意的一件事是 image/pjpeg

image/x-png
 是非标准的。在我的代码中,我将它们替换为 
image/jpeg
image/png


11
投票
如果你知道这是一个

System.Drawing.Image

,你可以这样做:

public static string GetMimeTypeFromImageByteArray(byte[] byteArray) { using (MemoryStream stream = new MemoryStream(byteArray)) using (Image image = Image.FromStream(stream)) { return ImageCodecInfo.GetImageEncoders().First(codec => codec.FormatID == image.RawFormat.Guid).MimeType; } }
    

10
投票
不确定,但也许你应该调查一下

幻数

更新: 读了它,我认为它不是很可靠。


8
投票
您无法从字节流中获知它,但您可以在最初填充

byte[]

 时存储 MIME 类型。


8
投票
如果您知道文件名的扩展名,则 System.Web.MimeMapping 可能可以解决问题:

MimeMapping.GetMimeMapping(fileDisplayNameWithExtension)

我在 MVC Action 中使用它,如下所示:

return File(fileDataByteArray, MimeMapping.GetMimeMapping(fileDisplayNameWithExtension), fileDisplayNameWithExtension);
    

7
投票
简短回答:你不能

更长的答案:通常,程序使用文件扩展名来了解它们正在处理的文件类型。如果您没有该扩展名,则只能进行猜测...例如,您可以查看前几个字节并检查您是否识别众所周知的标头(例如 XML 声明标记,或者位图或 JPEG 标头) )。但这最终永远是一个猜测:如果没有一些元数据或有关内容的信息,字节数组就没有意义......


4
投票
让我想起以前我们,呃,“有些人”曾经在早期的免费图像托管网站上共享 50MB rar 文件,只需在 .rar 文件名中添加 .gif 扩展名即可。

显然,如果您面向公众并且您期望某种文件类型,并且您必须确保它是该文件类型,那么您不能只信任该扩展名。

另一方面,如果您的应用程序没有理由不信任上传的扩展名和/或 MIME 类型,那么只需在上传文件时获取这些扩展名和/或 MIME 类型,就像您从 @rossfabircant 和 @RandolphPotter 收到的答案一样。 创建一个具有 byte[] 的类型以及原始扩展名或 mimetype,并将其传递。

如果您需要验证文件实际上是某种预期类型(例如有效的 .jpeg 或 .png),您可以尝试将文件解释为这些类型并查看它是否成功打开。 (系统.绘图.成像.图像格式)

如果您尝试仅从二进制内容对文件进行分类,并且它可以是全世界的任何格式,那么这确实是一个棘手的开放式问题,并且没有 100% 可靠的方法来做到这一点。 您可以调用

TrID 来对抗它,如果您能找到(并负担得起)执法调查人员可能会使用类似的取证工具。

如果你不必采取困难的方式,就不要这样做。


0
投票
你不想那样做。文件上传时调用 Path.GetExtension,并使用 byte[] 传递扩展名。


0
投票
如果您想要支持的预期文件类型数量有限,则可以使用幻数。

一种简单的检查方法是使用文本/十六进制编辑器打开示例文件,并研究前导字节,看看是否有一些东西可以用来区分/丢弃受支持的文件集。

另一方面,如果您希望识别任意文件类型,是的,正如每个人都已经说过的那样,很难。


0
投票
使用 System.Drawing.Image 'RawFormat.Guid' 属性,您可以检测图像的 MIME 类型。

但我不知道如何查找其他文件类型。

http://www.java2s.com/Code/CSharp/Network/GetImageMimeType.htm

更新:你可以尝试看看这篇文章

使用.NET,如何根据文件签名而不是扩展名找到文件的mime类型


0
投票
我在使用其他答案访问内存时得到了

AccessViolationException

,所以我使用此代码解决了我的问题:

[DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)] private static extern int FindMimeFromData(IntPtr pBc, [MarshalAs(UnmanagedType.LPWStr)] string pwzUrl, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)] byte[] pBuffer, int cbSize, [MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed, int dwMimeFlags, out IntPtr ppwzMimeOut, int dwReserved ); /** * This function will detect mime type from provided byte array * and if it fails, it will return default mime type */ private static string GetMimeFromBytes(byte[] dataBytes, string defaultMimeType) { if (dataBytes == null) throw new ArgumentNullException(nameof(dataBytes)); var mimeType = string.Empty; IntPtr suggestPtr = IntPtr.Zero, filePtr = IntPtr.Zero; try { var ret = FindMimeFromData(IntPtr.Zero, null, dataBytes, dataBytes.Length, null, 0, out var outPtr, 0); if (ret == 0 && outPtr != IntPtr.Zero) { mimeType = Marshal.PtrToStringUni(outPtr); Marshal.FreeCoTaskMem(outPtr); } } catch { mimeType = defaultMimeType; } return mimeType; }
如何称呼:

string ContentType = GetMimeFromBytes(byteArray, "image/jpeg");
希望这有帮助!


0
投票
这些是文件的签名,也许你可以检查一下。 例如,每个 PDF 文件都以“JVBERi0”开头

private readonly List<string> _allowedFileBase64StartWith = new List<string>() { "JVBERi0", // PDF file signature "0M8R4KGx", // XLS file signature "UEsDB", // XLSX file signature "0M8R4KGx", // DOC file signature "UEsDB" // DOCX file signature };
    
© www.soinside.com 2019 - 2024. All rights reserved.