我有一个从上传的文件填充的字节数组。但是,在代码的另一部分,我需要知道从 byte[] 上传的文件类型,以便我可以向浏览器呈现正确的内容类型!
谢谢!!
如前所述,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
。
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;
}
}
幻数。
更新: 读了它,我认为它不是很可靠。
byte[]
时存储 MIME 类型。
MimeMapping.GetMimeMapping(fileDisplayNameWithExtension)
我在 MVC Action 中使用它,如下所示:
return File(fileDataByteArray, MimeMapping.GetMimeMapping(fileDisplayNameWithExtension), fileDisplayNameWithExtension);
更长的答案:通常,程序使用文件扩展名来了解它们正在处理的文件类型。如果您没有该扩展名,则只能进行猜测...例如,您可以查看前几个字节并检查您是否识别众所周知的标头(例如 XML 声明标记,或者位图或 JPEG 标头) )。但这最终永远是一个猜测:如果没有一些元数据或有关内容的信息,字节数组就没有意义......
显然,如果您面向公众并且您期望某种文件类型,并且您必须确保它是该文件类型,那么您不能只信任该扩展名。
另一方面,如果您的应用程序没有理由不信任上传的扩展名和/或 MIME 类型,那么只需在上传文件时获取这些扩展名和/或 MIME 类型,就像您从 @rossfabircant 和 @RandolphPotter 收到的答案一样。 创建一个具有 byte[] 的类型以及原始扩展名或 mimetype,并将其传递。
如果您需要验证文件实际上是某种预期类型(例如有效的 .jpeg 或 .png),您可以尝试将文件解释为这些类型并查看它是否成功打开。 (系统.绘图.成像.图像格式)
如果您尝试仅从二进制内容对文件进行分类,并且它可以是全世界的任何格式,那么这确实是一个棘手的开放式问题,并且没有 100% 可靠的方法来做到这一点。 您可以调用
TrID 来对抗它,如果您能找到(并负担得起)执法调查人员可能会使用类似的取证工具。
如果你不必采取困难的方式,就不要这样做。
一种简单的检查方法是使用文本/十六进制编辑器打开示例文件,并研究前导字节,看看是否有一些东西可以用来区分/丢弃受支持的文件集。
另一方面,如果您希望识别任意文件类型,是的,正如每个人都已经说过的那样,很难。
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");
希望这有帮助!
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
};