获取文件类型不使用文件扩展名 c#

Posted

技术标签:

【中文标题】获取文件类型不使用文件扩展名 c#【英文标题】:Get file type not using the file extension c# 【发布时间】:2019-02-06 13:57:35 【问题描述】:

我知道以前有人问过这个问题,但是这两种解决方案都不适合我。 我想知道上传到我的服务器(通过 .ashx)的文件是 .xlsx、.xls 还是 .csv 类型。

我尝试使用here 列出的幻数,但如果我将 .msi 的扩展名更改为 .xls,文件将被识别为 .xls... 以下代码说明了我所说的:

private bool IsValidFileType(HttpPostedFile file)

    using (var memoryStream = new MemoryStream())
    
        file.InputStream.CopyTo(memoryStream);
        byte[] buffer = memoryStream.ToArray();

        //Check exe and dll
        if (buffer[0] == 0x4D && buffer[1] == 0x5A)
        
            return false;
        

        //Check xlsx
        if (buffer.Length >= 3 &&
            buffer[0] == 0x50 && buffer[1] == 0x4B &&
            buffer[2] == 0x03 && buffer[3] == 0x04 ||
            buffer[0] == 0x50 && buffer[1] == 0x4B &&
            buffer[2] == 0x05 && buffer[3] == 0x06)
        
            return true;
        

        //Check xls
        if (buffer.Length >= 7 &&
            buffer[0] == 0xD0 && buffer[1] == 0xCF &&
            buffer[2] == 0x11 && buffer[3] == 0xE0 &&
            buffer[4] == 0xA1 && buffer[5] == 0xB1 &&
            buffer[6] == 0x1A && buffer[7] == 0xE1)
        
            return true;
        

        return false;
    

然后我尝试使用urlmon.dll,如下所示,但它仍将文件识别为 .xls

    [DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
    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);

    public static string GetMimeFromFile(string file)
    
        if (!File.Exists(file))
            throw new FileNotFoundException(file + " not found");

        int MaxContent = (int)new FileInfo(file).Length;
        if (MaxContent > 4096) MaxContent = 4096;
        FileStream fs = File.OpenRead(file);


        byte[] buf = new byte[MaxContent];
        fs.Read(buf, 0, MaxContent);
        fs.Close();
        int result = FindMimeFromData(IntPtr.Zero, file, buf, MaxContent, null, 0, out IntPtr mimeout, 0);

        if (result != 0)
            throw Marshal.GetExceptionForHR(result);
        string mime = Marshal.PtrToStringUni(mimeout);
        Marshal.FreeCoTaskMem(mimeout);
        return mime;
    

我在想也许我应该尝试使用某些库打开上传的文件,例如 ExcelDataReader,但我不确定这是否是最好的方法。

任何帮助将不胜感激。

【问题讨论】:

为什么不能检查扩展名然后验证适当的魔术字节? (尽管后者不适用于 csv) 只是为了清楚起见:是否足以检测文件是否不是扩展名所说的应该是什么,或者您实际上需要检测它是什么类型的文件是否完全不考虑扩展? @AlexK。我目前正在这样做,但是,如果我有一个 .msi 文件,然后将其扩展名更改为 .xls,即使我检查了适当的魔术字节,结果仍然是相同的(似乎 .msi 头字节与.xls) @Fildor 我无法信任文件扩展名,因为用户可以上传带有 .xls 扩展名的 .msi 是的,我明白了。所以你只需要“欺诈检测”。如果实际文件类型 != ext 文件类型,取消操作并返回错误是可以的,对吧? 【参考方案1】:

EPPlusInterop 的 Excel 文件如何打开,如果不是 Excel 文件则捕获异常

FileInfo fileInfo = new FileInfo(filePath);
ExcelPackage package = null;
try

    package = new ExcelPackage(fileInfo);

catch(Exception exception)


或者有一个3rd party(未测试)来验证文件的类型。

FileInfo file = new FileInfo("C:\Hello.pdf");
if ( file.isExcel())
    Console.WriteLine("File is PDF");

【讨论】:

打开文件并捕获异常会非常慢。 @CodingYoshi 首先,我们讨论的是用户提供无效文件的极端情况。所以抛出和处理异常是正确的做法。此外,代码是明确的,它清楚地显示了一个无效的状态,这更便于维护。另外,我认为您对异常有一些预先判断,这不像您将它们扔进一个循环之类的......您实际上会在上传的执行中增加几毫秒,这是您对极端的定义吗? @marioz 我对异常没有预判,并且理解它不会增加很多时间。它正在打开文件,这将非常耗时。例外将增加这一点。如果用户正在添加多个文件并且您打开每个文件进行验证,那肯定会很慢。去试试吧。我的评论是提醒和仅供参考。 @CodingYoshi 只是打开文件的流根本不费时间。执行取决于您正在使用它做什么,例如是否计划读取整个文件?如果是这样,那么是的,当然会有一些处罚。 我最终使用了你发布的第三方库的一个分支 link 并且它工作得很好【参考方案2】:

文件本身就是数据。文件扩展名允许您的系统相应地解释该数据。如果没有文件扩展名,就无法绝对确定您正在查看的文件类型。 (除非您使用的是有限的文件类型子集)

但是,您可以从数据中推断出它可能是哪个文件扩展名。 Thierry V 引用的项目已过时且未得到维护。

您可能希望查看像 TrID 这样的工具,它使用不断增长的文件类型库。 该工具将分析文件并给出最可能的文件类型的排名。 就像我之前说的,它只能有限地告诉你它可能是哪种文件类型。

【讨论】:

【参考方案3】:

我尝试使用此处列出的幻数,但如果我将 .msi 的扩展名更改为 .xls,该文件将被识别为 .xls... 以下代码说明了我所说的:

是的,您在检查文件签名时唯一可以确定的是文件所基于的格式。因此,对于“.xls”文件,您将检测到该文件是复合二进制格式。但是,正如您注意到的,这种格式用于“.msi”文件,但也用于“.doc”、“.ppt”等。

此外,您的“.xlsx”检测也是如此,它只是检查文件是否为 zip 格式,并且将在“.zip”、“.docx”、“.ods”中找到相同的签名"等。

所以,您可以检查文件的签名并传递这两种格式的文件,但是“.csv”呢?在这里,您可以有各种字节值,因为它只是一个纯文本,没有签名。

无论如何,我认为真正的问题是您对这些 Excel 文件的目标是什么?您需要进一步处理它们还是什么? 如果您需要进一步处理它们,那么您应该依赖正在读取该文件的失败机制。因此,无论您选择哪个库来读取文件,很可能都会因为文件的“无法识别的格式”或“无法识别的结构”而引发异常。

我所说的“无法识别的结构”是指,例如在“.xls”文件中,它应该有名为“Workbook”、“SummaryInformation”等的流。

【讨论】:

以上是关于获取文件类型不使用文件扩展名 c#的主要内容,如果未能解决你的问题,请参考以下文章

如何在不基于扩展的 IOS 中获取 Mime 类型

C# 判断MP3格式(除扩展名)

从内容而不是从扩展名中获取文件类型[重复]

上传文件时获取不正确的文件扩展名和内容类型

在 C# 中找出确切的文件类型 [重复]

从 C 中的文件名获取 MIME 类型