从文件扩展名获取MIME类型

481

我怎样可以通过文件扩展名获取 MIME 类型?


16
我认为最好的选择是访问 http://msdn.microsoft.com/en-us/library/system.web.mimemapping.aspx。 - Yuki
3
@Yuki,感谢您添加了新的相关信息。不幸的是,微软的实现只有195个映射,无法添加更多,并且不可配置,该实现仅在.NET 4.5中可用。我的列表下面有560多个映射。奇怪的是,微软的列表甚至缺少一些非常常见的扩展名,例如.docx和.xlsx以及其他重要的Web扩展名,例如.csv、.swf和.air。 - Samuel Neff
提示:使用二进制分析来检测真实的文件格式,并确定MIME类型,不要仅依赖文件扩展名。 - Jeremy Thompson
30个回答

7

大部分解决方案都很好用,但为什么要花这么多的精力呢?我们同样可以轻松地获取MIME类型。

在System.Web程序集中,有一个方法可以从文件名获得MIME类型。

例如:

string mimeType = MimeMapping.GetMimeMapping(filename);

9
3年前,就在这个页面上,已经给出了同样的答案。https://dev59.com/tXNA5IYBdhLWcg3wQ7i4#14108040 - Null Head

7
为了让文章更加全面,对于.NET Core开发人员来说,有一个名为FileExtensionContentTypeProvider的类,它包含了官方MIME内容类型
它在幕后工作——根据文件扩展名设置Http响应头中的ContentType。
如果需要特殊的MIME类型,请参见自定义MIME类型示例
public void Configure(IApplicationBuilder app)
{
    // Set up custom content types -associating file extension to MIME type
    var provider = new FileExtensionContentTypeProvider();
    // Add new mappings
    provider.Mappings[".myapp"] = "application/x-msdownload";
    provider.Mappings[".htm3"] = "text/html";
    provider.Mappings[".image"] = "image/png";
    // Replace an existing mapping
    provider.Mappings[".rtf"] = "application/x-msdownload";
    // Remove MP4 videos.
    provider.Mappings.Remove(".mp4");

    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot", "images")),
        RequestPath = new PathString("/MyImages"),
        ContentTypeProvider = provider
    });

    app.UseDirectoryBrowser(new DirectoryBrowserOptions()
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot", "images")),
        RequestPath = new PathString("/MyImages")
    });
}

1
感谢您让我们知道这个问题。给您一个提示:要获取 Web 应用程序的根文件夹,您应该使用 _hostingEnvironment.WebRootPath - 参见此处 - Sven

5

".Net框架提供了内置方法来获取MIME类型。无需编写自定义代码。"

"string mimeType = System.Web.MimeMapping.GetMimeMapping(fileName);"


3
使用MimeTypeMap包,它提供了大量的双向映射,将文件扩展名映射到MIME类型和MIME类型映射到文件扩展名。
使用MimeTypes;
获取扩展名的MIME类型。
Console.WriteLine("txt -> " + MimeTypeMap.GetMimeType("txt"));  // "text/plain"

获取MIME类型的扩展名
Console.WriteLine("audio/wav -> " + MimeTypeMap.GetExtension("audio/wav")); // ".wav"

GitHub链接:https://github.com/samuelneff/MimeTypeMap


2

受 Samuel 答案的启发,我写了一个改进版:

  • 当扩展名为大写时也有效。
  • 以文件名作为输入,优雅地处理没有扩展名的文件。
  • 在键中不要包含“.”
  • 来自Apache的列表,为此我编写了一个小型转换脚本

最终的源代码超过30K字符,所以我无法在这里发布它,请在Github上查看。


2

** 使用 MediaTypeNames 类-->示例 : MediaTypeNames.Application.Pdf ** 输入图像描述


1
虽然MediaTypeNames在使用HttpResponseMessage返回Web API中的内容时需要避免使用魔法字符串,但实际上它与OP所要求的相反。 - nrodic

2

您不应该相信来自客户端的文件扩展名。始终检查文件的魔数。

在ASP.NET Core中使用FileTpeInterrogator

public static class FileTypeChecker
{
    private static List<string> validVideoMimeTypes = new List<string> { "video/mp4", "video/quicktime" };
    private static List<string> validImageMimeTypes = new List<string> { "image/png", "image/jpeg" };

    public static bool IsValidVideo(IFormFile file)
    {
        return validVideoMimeTypes.Contains(GetFileMimeType(file));
    }

    public static bool IsValidImage(IFormFile file)
    {
        return validImageMimeTypes.Contains(GetFileMimeType(file));
    }

    private static string GetFileMimeType(IFormFile file)
    {
        // You should have checked for null and file length before reaching here

        IFileTypeInterrogator interrogator = new FileTypeInterrogator.FileTypeInterrogator();

        byte[] fileBytes;
        using (var stream = new MemoryStream())
        {
            file.CopyTo(stream);
            fileBytes = stream.ToArray();
        }

        FileTypeInfo fileTypeInfo = interrogator.DetectType(fileBytes);
        return fileTypeInfo.MimeType.ToLower();
    }
}

在你的控制器或服务中:

public IActionResult UploadVideo([FromForm] UploadVideoVM model)
{
    if (model.File.Length < minimumLength || model.File.Length > maximumLength)
    {
        // BadRequest => Size
    }
    else if (!FileTypeChecker.IsValidVideo(model.File))
    {
        // BadRequest => Type
    }
    else
    {
        // All good
    }

    return Ok();
}

要获取文件扩展名的MIME类型,请参考此文件

1
这个Nuget存在一个漏洞问题:https://nvd.nist.gov/vuln/detail/CVE-2014-8117,有什么替代方案吗? - VJPPaz

2

通过使用Apache列表,以下脚本将为您提供包含所有MIME类型的字典。

var mimeTypeListUrl = "http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types";
var webClient = new WebClient();
var rawData = webClient.DownloadString(mimeTypeListUrl).Split(new[] { Environment.NewLine, "\n" }, StringSplitOptions.RemoveEmptyEntries);

var extensionToMimeType = new Dictionary<string, string>();
var mimeTypeToExtension = new Dictionary<string, string[]>();

foreach (var row in rawData)
{
    if (row.StartsWith("#")) continue;

    var rowData = row.Split(new[] { "\t" }, StringSplitOptions.RemoveEmptyEntries);
    if (rowData.Length != 2) continue;

    var extensions = rowData[1].Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
    if (!mimeTypeToExtension.ContainsKey(rowData[0]))
    {
        mimeTypeToExtension.Add(rowData[0], extensions);
    }

    foreach (var extension in extensions)
    {
        if (!extensionToMimeType.ContainsKey(extension))
        {
            extensionToMimeType.Add(extension, rowData[0]);
        }
    }

}

1
这个帮助类能够返回任何文件名的MIME类型(内容类型)、描述和图标:
using Microsoft.Win32;
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;

public static class Helper
{
    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    private static extern int ExtractIconEx(string lpszFile, int nIconIndex, IntPtr[] phIconLarge, IntPtr[] phIconSmall, int nIcons);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern int DestroyIcon(IntPtr hIcon);

    public static string GetFileContentType(string fileName)
    {
        if (fileName == null)
        {
            throw new ArgumentNullException("fileName");
        }

        RegistryKey registryKey = null;
        try
        {
            FileInfo fileInfo = new FileInfo(fileName);

            if (string.IsNullOrEmpty(fileInfo.Extension))
            {
                return string.Empty;
            }

            string extension = fileInfo.Extension.ToLowerInvariant();

            registryKey = Registry.ClassesRoot.OpenSubKey(extension);
            if (registryKey == null)
            {
                return string.Empty;
            }

            object contentTypeObject = registryKey.GetValue("Content Type");
            if (!(contentTypeObject is string))
            {
                return string.Empty;
            }

            string contentType = (string)contentTypeObject;

            return contentType;
        }
        catch (Exception)
        {
            return null;
        }
        finally
        {
            if (registryKey != null)
            {
                registryKey.Close();
            }
        }
    }

    public static string GetFileDescription(string fileName)
    {
        if (fileName == null)
        {
            throw new ArgumentNullException("fileName");
        }

        RegistryKey registryKey1 = null;
        RegistryKey registryKey2 = null;
        try
        {
            FileInfo fileInfo = new FileInfo(fileName);

            if (string.IsNullOrEmpty(fileInfo.Extension))
            {
                return string.Empty;
            }

            string extension = fileInfo.Extension.ToLowerInvariant();

            registryKey1 = Registry.ClassesRoot.OpenSubKey(extension);
            if (registryKey1 == null)
            {
                return string.Empty;
            }

            object extensionDefaultObject = registryKey1.GetValue(null);
            if (!(extensionDefaultObject is string))
            {
                return string.Empty;
            }

            string extensionDefaultValue = (string)extensionDefaultObject;

            registryKey2 = Registry.ClassesRoot.OpenSubKey(extensionDefaultValue);
            if (registryKey2 == null)
            {
                return string.Empty;
            }

            object fileDescriptionObject = registryKey2.GetValue(null);
            if (!(fileDescriptionObject is string))
            {
                return string.Empty;
            }

            string fileDescription = (string)fileDescriptionObject;
            return fileDescription;
        }
        catch (Exception)
        {
            return null;
        }
        finally
        {
            if (registryKey2 != null)
            {
                registryKey2.Close();
            }

            if (registryKey1 != null)
            {
                registryKey1.Close();
            }
        }
    }

    public static void GetFileIcons(string fileName, out Icon smallIcon, out Icon largeIcon)
    {
        if (fileName == null)
        {
            throw new ArgumentNullException("fileName");
        }

        smallIcon = null;
        largeIcon = null;

        RegistryKey registryKey1 = null;
        RegistryKey registryKey2 = null;
        try
        {
            FileInfo fileInfo = new FileInfo(fileName);

            if (string.IsNullOrEmpty(fileInfo.Extension))
            {
                return;
            }

            string extension = fileInfo.Extension.ToLowerInvariant();

            registryKey1 = Registry.ClassesRoot.OpenSubKey(extension);
            if (registryKey1 == null)
            {
                return;
            }

            object extensionDefaultObject = registryKey1.GetValue(null);
            if (!(extensionDefaultObject is string))
            {
                return;
            }

            string defaultIconKeyName = string.Format("{0}\\DefaultIcon", extensionDefaultObject);

            registryKey2 = Registry.ClassesRoot.OpenSubKey(defaultIconKeyName);
            if (registryKey2 == null)
            {
                return;
            }

            object defaultIconPathObject = registryKey2.GetValue(null);
            if (!(defaultIconPathObject is string))
            {
                return;
            }

            string defaultIconPath = (string)defaultIconPathObject;
            if (string.IsNullOrWhiteSpace(defaultIconPath))
            {
                return;
            }

            string iconfileName = null;
            int iconIndex = 0;

            int commaIndex = defaultIconPath.IndexOf(",");
            if (commaIndex > 0)
            {
                iconfileName = defaultIconPath.Substring(0, commaIndex);
                string iconIndexString = defaultIconPath.Substring(commaIndex + 1);

                if (!int.TryParse(iconIndexString, out iconIndex))
                {
                    iconIndex = 0;
                }
            }
            else
            {
                iconfileName = defaultIconPath;
                iconIndex = 0;
            }

            IntPtr[] phiconSmall = new IntPtr[1] { IntPtr.Zero };
            IntPtr[] phiconLarge = new IntPtr[1] { IntPtr.Zero };

            int readIconCount = ExtractIconEx(iconfileName, iconIndex, phiconLarge, phiconSmall, 1);

            if (readIconCount < 0)
            {
                return;
            }

            if (phiconSmall[0] != IntPtr.Zero)
            {
                smallIcon = (Icon)Icon.FromHandle(phiconSmall[0]).Clone();
                DestroyIcon(phiconSmall[0]);
            }

            if (phiconLarge[0] != IntPtr.Zero)
            {
                largeIcon = (Icon)Icon.FromHandle(phiconLarge[0]).Clone();
                DestroyIcon(phiconLarge[0]);
            }

            return;
        }
        finally
        {
            if (registryKey2 != null)
            {
                registryKey2.Close();
            }

            if (registryKey1 != null)
            {
                registryKey1.Close();
            }
        }
    }
}

使用方法:
string fileName = "NotExists.txt";
string contentType = Helper.GetFileContentType(fileName); // "text/plain"
string description = Helper.GetFileDescription(fileName); // "Text Document"
Icon smallIcon, largeIcon;
Helper.GetFileIcons(fileName, out smallIcon, out largeIcon); // 16x16, 32x32 icons

不幸的是,这种方法在跨平台上无法工作,并且由于使用了RegistryKeys,在云中可能会很容易引起问题。 - Dave Black

1

你可以使用Apache的httpd提供的表格。将其映射成函数、字典、列表等应该很容易。

另外,如此处所示,扩展名->MIME类型并非一一对应关系。一个文件扩展名可能有多个常见的MIME类型,因此你需要考虑你的应用程序的需求,并思考你为什么关心MIME类型,想要对它们做些什么等问题。你是否可以使用文件扩展名来实现相同的操作?你是否需要读取文件的前几个字节来确定它的MIME类型?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接