我正在寻找一种简单的方法来获得mime类型,其中文件扩展名是不正确的或没有给出,类似于这个问题只有在. net。


当前回答

iis7或以上

使用此代码,但您需要是服务器上的管理员

public bool CheckMimeMapExtension(string fileExtension)
        {
            try
            {

                using (
                ServerManager serverManager = new ServerManager())
                {   
                    // connects to default app.config
                    var config = serverManager.GetApplicationHostConfiguration();
                    var staticContent = config.GetSection("system.webServer/staticContent");
                    var mimeMap = staticContent.GetCollection();

                    foreach (var mimeType in mimeMap)
                    {

                        if (((String)mimeType["fileExtension"]).Equals(fileExtension, StringComparison.OrdinalIgnoreCase))
                            return true;

                    }

                }
                return false;
            }
            catch (Exception ex)
            { 
                Console.WriteLine("An exception has occurred: \n{0}", ex.Message);
                Console.Read();
            }

            return false;

        }

其他回答

I think the right answer is a combination of Steve Morgan's and Serguei's answers. That's how Internet Explorer does it. The pinvoke call to FindMimeFromData works for only 26 hard-coded mime types. Also, it will give ambigous mime types (such as text/plain or application/octet-stream) even though there may exist a more specific, more appropriate mime type. If it fails to give a good mime type, you can go to the registry for a more specific mime type. The server registry could have more up-to-date mime types.

参考网址:http://msdn.microsoft.com/en-us/library/ms775147(VS.85).aspx

这个答案是作者的答案(Richard Gourlay)的副本,但改进了解决iis8 / win2012上的问题(其中函数会导致应用程序池崩溃),基于Rohland的评论指向http://www.pinvoke.net/default.aspx/urlmon.findmimefromdata

using System.Runtime.InteropServices;

...

public static string GetMimeFromFile(string filename)
{

    if (!File.Exists(filename))
        throw new FileNotFoundException(filename + " not found");

    const int maxContent = 256;

    var buffer = new byte[maxContent];
    using (var fs = new FileStream(filename, FileMode.Open))
    {
        if (fs.Length >= maxContent)
            fs.Read(buffer, 0, maxContent);
        else
            fs.Read(buffer, 0, (int) fs.Length);
    }

    var mimeTypePtr = IntPtr.Zero;
    try
    {
        var result = FindMimeFromData(IntPtr.Zero, null, buffer, maxContent, null, 0, out mimeTypePtr, 0);
        if (result != 0)
        {
            Marshal.FreeCoTaskMem(mimeTypePtr);
            throw Marshal.GetExceptionForHR(result);
        }

        var mime = Marshal.PtrToStringUni(mimeTypePtr);
        Marshal.FreeCoTaskMem(mimeTypePtr);
        return mime;
    }
    catch (Exception e)
    {
        if (mimeTypePtr != IntPtr.Zero)
        {
            Marshal.FreeCoTaskMem(mimeTypePtr);
        }
        return "unknown/unknown";
    }
}

[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);

@Steve Morgan和@Richard Gourlay,这是一个很好的解决方案,谢谢你们。一个小缺点是,当文件中的字节数为255或以下时,mime类型有时会产生“application/octet-stream”,这对于期望产生“text/plain”的文件来说有点不准确。对于这种情况,我更新了你原来的方法如下:

如果文件中的字节数小于或等于255,并且推导出的mime类型是"application/octet-stream",那么创建一个新的字节数组,该数组由重复n次的原始文件字节组成,直到字节总数为>= 256。然后重新检查新字节数组上的mime-type。

修改方法:

Imports System.Runtime.InteropServices

<DllImport("urlmon.dll", CharSet:=CharSet.Auto)> _
Private Shared Function FindMimeFromData(pBC As System.UInt32, <MarshalAs(UnmanagedType.LPStr)> pwzUrl As System.String, <MarshalAs(UnmanagedType.LPArray)> pBuffer As Byte(), cbSize As System.UInt32, <MarshalAs(UnmanagedType.LPStr)> pwzMimeProposed As System.String, dwMimeFlags As System.UInt32, _
ByRef ppwzMimeOut As System.UInt32, dwReserverd As System.UInt32) As System.UInt32
End Function
Private Function GetMimeType(ByVal f As FileInfo) As String
    'See http://stackoverflow.com/questions/58510/using-net-how-can-you-find-the-mime-type-of-a-file-based-on-the-file-signature
    Dim returnValue As String = ""
    Dim fileStream As FileStream = Nothing
    Dim fileStreamLength As Long = 0
    Dim fileStreamIsLessThanBByteSize As Boolean = False

    Const byteSize As Integer = 255
    Const bbyteSize As Integer = byteSize + 1

    Const ambiguousMimeType As String = "application/octet-stream"
    Const unknownMimeType As String = "unknown/unknown"

    Dim buffer As Byte() = New Byte(byteSize) {}
    Dim fnGetMimeTypeValue As New Func(Of Byte(), Integer, String)(
        Function(_buffer As Byte(), _bbyteSize As Integer) As String
            Dim _returnValue As String = ""
            Dim mimeType As UInt32 = 0
            FindMimeFromData(0, Nothing, _buffer, _bbyteSize, Nothing, 0, mimeType, 0)
            Dim mimeTypePtr As IntPtr = New IntPtr(mimeType)
            _returnValue = Marshal.PtrToStringUni(mimeTypePtr)
            Marshal.FreeCoTaskMem(mimeTypePtr)
            Return _returnValue
        End Function)

    If (f.Exists()) Then
        Try
            fileStream = New FileStream(f.FullName(), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
            fileStreamLength = fileStream.Length()

            If (fileStreamLength >= bbyteSize) Then
                fileStream.Read(buffer, 0, bbyteSize)
            Else
                fileStreamIsLessThanBByteSize = True
                fileStream.Read(buffer, 0, CInt(fileStreamLength))
            End If

            returnValue = fnGetMimeTypeValue(buffer, bbyteSize)

            If (returnValue.Equals(ambiguousMimeType, StringComparison.OrdinalIgnoreCase) AndAlso fileStreamIsLessThanBByteSize AndAlso fileStreamLength > 0) Then
                'Duplicate the stream content until the stream length is >= bbyteSize to get a more deterministic mime type analysis.
                Dim currentBuffer As Byte() = buffer.Take(fileStreamLength).ToArray()
                Dim repeatCount As Integer = Math.Floor((bbyteSize / fileStreamLength) + 1)
                Dim bBufferList As List(Of Byte) = New List(Of Byte)
                While (repeatCount > 0)
                    bBufferList.AddRange(currentBuffer)
                    repeatCount -= 1
                End While
                Dim bbuffer As Byte() = bBufferList.Take(bbyteSize).ToArray()
                returnValue = fnGetMimeTypeValue(bbuffer, bbyteSize)
            End If
        Catch ex As Exception
            returnValue = unknownMimeType
        Finally
            If (fileStream IsNot Nothing) Then fileStream.Close()
        End Try
    End If
    Return returnValue
End Function

iis7或以上

使用此代码,但您需要是服务器上的管理员

public bool CheckMimeMapExtension(string fileExtension)
        {
            try
            {

                using (
                ServerManager serverManager = new ServerManager())
                {   
                    // connects to default app.config
                    var config = serverManager.GetApplicationHostConfiguration();
                    var staticContent = config.GetSection("system.webServer/staticContent");
                    var mimeMap = staticContent.GetCollection();

                    foreach (var mimeType in mimeMap)
                    {

                        if (((String)mimeType["fileExtension"]).Equals(fileExtension, StringComparison.OrdinalIgnoreCase))
                            return true;

                    }

                }
                return false;
            }
            catch (Exception ex)
            { 
                Console.WriteLine("An exception has occurred: \n{0}", ex.Message);
                Console.Read();
            }

            return false;

        }

我使用混合解决方案:

    using System.Runtime.InteropServices;

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

    private string GetMimeFromRegistry (string Filename)
    {
        string mime = "application/octetstream";
        string ext = System.IO.Path.GetExtension(Filename).ToLower();
        Microsoft.Win32.RegistryKey rk = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
        if (rk != null && rk.GetValue("Content Type") != null)
            mime = rk.GetValue("Content Type").ToString();
        return mime;
    }

    public string GetMimeTypeFromFileAndRegistry (string filename)
    {
        if (!File.Exists(filename))
        {
           return GetMimeFromRegistry (filename);
        }

        byte[] buffer = new byte[256];

        using (FileStream fs = new FileStream(filename, FileMode.Open))
        {
            if (fs.Length >= 256)
                fs.Read(buffer, 0, 256);
            else
                fs.Read(buffer, 0, (int)fs.Length);
        }

        try
        {            
            System.UInt32 mimetype;

            FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0);

            System.IntPtr mimeTypePtr = new IntPtr(mimetype);

            string mime = Marshal.PtrToStringUni(mimeTypePtr);

            Marshal.FreeCoTaskMem(mimeTypePtr);

            if (string.IsNullOrWhiteSpace (mime) || 
                mime =="text/plain" || mime == "application/octet-stream")                    
            {
                return GetMimeFromRegistry (filename);
            }

            return mime;
        }
        catch (Exception e)
        {
            return GetMimeFromRegistry (filename);
        }
    }