如何在Python中查找文件的MIME类型?

266

假设你想将一堆文件保存在某个地方,比如说在BLOB中。并且你想通过网页分发这些文件,并让客户端自动打开正确的应用程序/查看器。

假设:浏览器通过HTTP响应中的mime-type(content-type?)头来确定要使用哪个应用程序/查看器。

基于这一假设,除了文件的字节之外,您还需要保存MIME类型。

如何找到文件的MIME类型?我目前在Mac上,但这也适用于Windows。

当将文件发布到网页时,浏览器是否会添加此信息?

是否有一个漂亮的Python库来查找这些信息?一个Web服务或(更好的是)可下载的数据库?

19个回答

7
您没有说明您使用的是哪个Web服务器,但Apache有一个很好的小模块叫做Mime Magic,它用于在被告知时确定文件类型。它读取一些文件内容并尝试根据找到的字符来确定文件类型。正如Dave Webb提到的那样,Python下的MimeTypes Module也可以工作,只要有一个扩展名就行了。
或者,如果您正在UNIX系统上,可以使用sys.popen('file -i ' + fileName, mode='r')来获取MIME类型。Windows应该有一个相当的命令,但我不确定是什么。

8
现在你可以使用subprocess.check_output(['file', '-b', '--mime', filename])来执行操作。 - Nathan Villaescusa
当Python-magic可以提供等效的功能时,真的没有理由使用外部工具,一切都包含在其中,非常方便。 - damd

7

Python 3 参考文档:https://docs.python.org/3.2/library/mimetypes.html

mimetypes.guess_type(url, strict=True)根据文件名或URL(由url给出)猜测文件类型。返回值是一个元组(type,encoding),其中type如果无法猜测(缺少或未知后缀)则为None, 或者形如'type/subtype'的字符串,可用于MIME内容类型头。

如果没有编码,则encoding为None或用于编码的程序的名称(例如compress或gzip)。编码适用于Content-Encoding标头,而不适用于Content-Transfer-Encoding标头。 映射是表驱动的。编码后缀区分大小写;首先使用区分大小写的类型后缀,然后使用不区分大小写的类型后缀。

可选的strict参数是一个标志,指定已知MIME类型列表是否仅限于与IANA注册的正式类型。当strict为True(默认值)时,仅支持IANA类型; 当strict为False时,还会识别一些额外的非标准但常用的MIME类型。

import mimetypes
print(mimetypes.guess_type("sample.html"))

6

在 Python 3.x 和 Web 应用程序中,如果 URL 对应的文件没有扩展名或者有伪造的扩展名,您需要安装 python-magic 模块。

pip3 install python-magic

对于 Mac OS X,您还应该使用以下命令安装 libmagic

brew install libmagic

代码片段

import urllib
import magic
from urllib.request import urlopen

url = "http://...url to the file ..."
request = urllib.request.Request(url)
response = urlopen(request)
mime_type = magic.from_buffer(response.readline())
print(mime_type)

或者您可以在读取时添加一个大小

import urllib
import magic
from urllib.request import urlopen

url = "http://...url to the file ..."
request = urllib.request.Request(url)
response = urlopen(request)
mime_type = magic.from_buffer(response.read(128))
print(mime_type)

它会加载整个文件吗? - ife
不是的,它是一个流,通常只有几个字节。 - Claude COULOMBE
我已经编辑了我的response.readline()或response.read(128)。谢谢! - Claude COULOMBE

4

我首先尝试使用mimetypes库。如果不起作用,我会使用python-magic库。

import mimetypes
def guess_type(filename, buffer=None):
mimetype, encoding = mimetypes.guess_type(filename)
if mimetype is None:
    try:
        import magic
        if buffer:
            mimetype = magic.from_buffer(buffer, mime=True)
        else:
            mimetype = magic.from_file(filename, mime=True)
    except ImportError:
        pass
return mimetype

2

mimetypes模块只能根据文件扩展名识别文件类型。如果您尝试恢复没有扩展名的文件类型,则mimetypes将无法正常工作。


3
我不认为那是正确的。MIME类型是关于如何告诉他人数据格式的,而不是关于如何自己找出数据格式的。如果你使用的工具仅基于扩展名猜测格式并打印MIME类型,那么如果没有文件扩展名,你将无法使用该工具。但还有其他猜测格式的方法,例如通过使用解析器检查。 - erikbstack

2

我很惊讶的是没有人提到,但Pygments能够对文本文件的mime-type做出合理的猜测。

Pygments实际上是一个Python语法高亮库,但它有一种方法能够推断出你的文档属于500种支持的文档类型之一。例如:c++ vs C# vs Python等。

import inspect

def _test(text: str):
    from pygments.lexers import guess_lexer
    lexer = guess_lexer(text)
    mimetype = lexer.mimetypes[0] if lexer.mimetypes else None
    print(mimetype)

if __name__ == "__main__":
    # Set the text to the actual defintion of _test(...) above
    text = inspect.getsource(_test)
    print('Text:')
    print(text)
    print()
    print('Result:')
    _test(text)

输出:

Text:
def _test(text: str):
    from pygments.lexers import guess_lexer
    lexer = guess_lexer(text)
    mimetype = lexer.mimetypes[0] if lexer.mimetypes else None
    print(mimetype)


Result:
text/x-python

现在,它不是完美的,但如果你需要能够确定正在使用的500种文档格式中的哪一种,这非常有用。


1
对于字节数组类型的数据,您可以使用magic.from_buffer(_byte_array,mime=True)。

0

我尝试了很多例子,但使用Django mutagen非常好用。

例如检查文件是否为mp3

from mutagen.mp3 import MP3, HeaderNotFoundError  

try:
    audio = MP3(file)
except HeaderNotFoundError:
    raise ValidationError('This file should be mp3')

缺点是您检查文件类型的能力受到限制,但如果您不仅想检查文件类型还想访问其他信息,这是一个很好的方法。


我需要检查安全性。 - Artem Bernatskyi

0

我在使用Python 3和MSYS2时,无法让任何一个magic实现模块正常工作。最终我选择调用file可执行文件并回退到mimetypes模块。

import mimetypes
import os
import subprocess

def getMimeType(filepath):
  mime_type = None
  # replace '<system_root>/usr/bin/file.exe' with path to 'file' executuble for your system
  if os.path.isfile(filepath) and os.path.isfile("<system_root>/usr/bin/file.exe"):
    res = subprocess.run(["/usr/bin/file", "--mime-type", "--brief", filepath], stdout=subprocess.PIPE)
    if res.stdout:
      mime_type = res.stdout.decode("utf-8")
  if not mime_type:
    # fallback to guessing by filename extension
    mime_type = mimetypes.guess_type(filepath)[0]
  return mime_type

请注意最好有一个函数来搜索PATH以查找“file”可执行文件,而不是在代码中硬编码它。
如果找不到可用的“magic”模块,您也可以将其用作后备方案。
__have_magic = False
try:
  import magic
  __have_magic = True
except ModuleNotFoundError:
  pass

...

def getMimeType(filepath):
  mime_type = None
  if __have_magic and os.path.isfile(filepath):
    mime_type = magic.from_file(filepath, mime=True)
  if not mime_type:
    # fallback to 'file' call
    ...

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