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

266

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

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

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

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

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

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

19个回答

302

来自toivotuo的python-magic方法已经过时。目前在Github上有一个基于readme的Python-magic,用于查找MIME类型的操作可以像这样进行。

# For MIME types
import magic
mime = magic.Magic(mime=True)
mime.from_file("testdata/test.pdf") # 'application/pdf'

23
谢谢您的留言!请注意,在Stack Overflow中,“above”是一个比较困难的概念,因为排序方式是按投票分组并在组内随机排序。我猜您指的是@toivotuo的答案。 - Daren Thomas
1
是的,我在写这个回复时没有足够的“积分”来创建评论。但我可能应该把它写成评论,这样@toivotuo就可以编辑他的问题了。 - Simon Klee
1
rpm -qf /usr/lib/python2.7/site-packages/magic.py -i URL:http://www.darwinsys.com/file/ 摘要:libmagic API的Python绑定rpm -qf /usr/bin/file -i 名称:file URL:http://www.darwinsys.com/file/来自http://www.darwinsys.com/file/的python-magic,与Linux Fedora一起使用就像@toivotuo所说的那样。似乎更加主流。 - Sérgio
8
请注意,名为python-magic的Debian/Ubuntu软件包与同名的pip软件包不同。两者都是用import magic引入,但它们包含的内容不兼容。详情请参见https://dev59.com/WnRC5IYBdhLWcg3wAcbU#16203777。 - Hamish Downer
1
正如我在toivotuo的回答中所评论的,它并不过时!你正在谈论一个不同的库。你能否请删除或替换你回答中的那个声明?它目前使得寻找最佳解决方案变得非常困难。 - bodo
显示剩余5条评论

126

标准库中的mimetypes模块可以通过文件扩展名确定/猜测MIME类型。

如果用户正在上传文件,则HTTP POST将包含数据以及文件的MIME类型。例如,Django将此数据作为UploadedFile对象的属性可用。


14
如果文件存储在BLOB中,正如问题所指定的那样,您可能不知道文件扩展名。 - Mechanical snail
74
文件扩展名并不是确定 MIME 类型的可靠方式。 - Cerin
16
import mimetypes 导入mimetypes模块mimetypes.MimeTypes().guess_type(filename)[0] 使用MimeTypes类猜测文件类型,并返回第一个猜测结果。 - Jonathan
6
在Python 3.6中,以下代码有效:mimetypes.guess_type(path_file_to_upload)[1] - JinSnow
6
尽管@cerin的说法正确,即文件扩展名不可靠,但我刚刚发现顶部答案中建议的python-magic的准确性甚至更低,这一点在https://github.com/s3tools/s3cmd/issues/198得到了证实。 因此,对我来说,mimetypes似乎是更好的选择。 - danqing
显示剩余5条评论

57

这似乎非常容易

>>> from mimetypes import MimeTypes
>>> import urllib 
>>> mime = MimeTypes()
>>> url = urllib.pathname2url('Upload.xml')
>>> mime_type = mime.guess_type(url)
>>> print mime_type
('application/xml', None)

请参考旧帖子

更新 - 在Python 3+版本中,现在更加方便:

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

5
在你的例子中,我认为 urllib 不是必需的。 - BrotherJack
6
对于Python 3.X版本,将“import urllib”替换为“from urllib import request”。然后使用“request”代替urllib。 - Arjun Thakur
1
也适用于Python 2.7 - Jay Modi
@oetzi的解决方案使用了这个模块,但更为简单。 - Garrett

55

使用python-magic包比使用mimetypes库更可靠。

import magic
m = magic.open(magic.MAGIC_MIME)
m.load()
m.file("/tmp/document.pdf")
这与使用 file(1) 等效。
在 Django 中,还可以确保 MIME 类型与 UploadedFile.content_type 匹配。

2
请查看Simon Zimmermann的帖子,了解python-magic的最新用法。 - Daren Thomas
如@mammadori所述,这个答案并不过时,并且与Simon Zimmermann的解决方案不同。如果您已经安装了文件实用程序,那么您可能可以使用此解决方案。它在我的file-5.32上运行良好。在gentoo上,您还必须启用文件包的python USE-flag。 - bodo

37

13年后...
这个页面上关于Python 3的大多数答案要么过时,要么不完整。
获取文件的MIME类型我使用以下代码:

import mimetypes

mt = mimetypes.guess_type("https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf")
if mt:
    print("Mime Type:", mt[0])
else:
    print("Cannot determine Mime Type")

# Mime Type: application/pdf

演示现场


来自Python文档

mimetypes.guess_type(url, strict=True)

根据文件名、路径或url(由字符串或类似路径的对象表示)猜测文件的类型。

返回值是一个元组(type, encoding),其中,如果无法猜测type(缺少后缀名或未知后缀名),则为None,否则为形如'type/subtype'的字符串,可用于MIME content-type头。

对于没有编码或使用的编码程序名称(例如compressgzip),encodingNone。编码适用于作为Content-Encoding头,但不适用作为Content-Transfer-Encoding头。映射使用表驱动。编码后缀区分大小写;首先以区分大小写的方式尝试类型后缀,然后以不区分大小写的方式尝试。

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

3.8版中的更改:添加了对url作为类似路径的对象的支持。


1
如果文件路径没有扩展名,则无法通过 MIME 获取正确的结果。 - HobbyMarks

17

Python绑定到libmagic

关于此主题的所有不同答案都非常令人困惑,因此我希望通过对libmagic的不同绑定进行概述来提供更多的清晰度。以前mammadori给出了一个简短的答案列出了可用的选项。

libmagic

在确定文件的MIME类型时,首选工具称为file,其后端称为libmagic。(请参见项目主页。)该项目在私有cvs存储库中开发,但是在github上有一个只读git镜像

现在,如果您想使用任何与Python绑定的libmagic绑定,就需要使用这个工具,它已经带有自己的Python绑定,称为file-magic。它们没有太多专门的文档,但是您可以随时查看c库的man页面:man libmagic。基本用法在readme文件中描述:
import magic

detected = magic.detect_from_filename('magic.py')
print 'Detected MIME type: {}'.format(detected.mime_type)
print 'Detected encoding: {}'.format(detected.encoding)
print 'Detected file type name: {}'.format(detected.name)

除此之外,您还可以通过使用magic.open(flags)创建Magic对象来使用库,如示例文件所示。 toivotuo和ewr2san都使用了包含在file工具中的这些file-magic绑定。他们错误地认为自己正在使用python-magic软件包。这似乎表明,如果安装了filepython-magic,则python模块magic将指向前者。

python-magic

这是Simon Zimmermann在his answer中提到的库,也是Claude COULOMBEGringo Suave所采用的库。

filemagic

注意: 该项目最后更新于2013年!

由于基于相同的c-api,因此该库与包含在libmagic中的file-magic具有一定的相似性。只有mammadori提到了它,其他答案都没有使用它。


15

2017年更新

无需访问Github,该项目已经以不同的名称发布在PyPi上:

pip3 install --user python-magic
# or:
sudo apt install python3-magic  # Ubuntu distro package

代码也可以简化:

>>> import magic

>>> magic.from_file('/tmp/img_3304.jpg', mime=True)
'image/jpeg'

你能否对JS或CSS文件做同样的事情? - kumbhani bhavesh
当然,为什么不呢? - Gringo Suave

11

有三个不同的库包装了 libmagic。

其中两个可在 pypi 上获得(因此 pip install 将起作用):

  • filemagic
  • python-magic

另一个类似于 python-magic 的库直接在最新的 libmagic 源中提供,这可能是您在 Linux 发行版中使用的库。

在 Debian 中,软件包 python-magic 大约是关于这个的,并且像 toivotuo 说的那样使用它,并且像 Simon Zimmermann 说的那样不过时(依我看)。

我认为这是原始作者的另一种方式。

很遗憾它没有直接在 pypi 上提供。


我为方便起见添加了一个仓库链接:https://github.com/mammadori/magic-python这样你就可以执行以下命令:pip install -e git://github.com/mammadori/magic-python.git#egg=Magic_file_extensions - mammadori

10

在Python 2.6中:

import shlex
import subprocess
mime = subprocess.Popen("/usr/bin/file --mime " + shlex.quote(PATH), shell=True, \
    stdout=subprocess.PIPE).communicate()[0]

7
这是不必要的,因为 file 命令基本上只是 libmagic 的一个包装器。你可以像 Simon 的回答中那样直接使用 python 绑定(python-magic)。 - Mechanical snail
7
这取决于操作系统。例如,在 Mac OS X 中,您有“文件”但在正常环境中没有libmagic。 - rptb1
这对我来说看起来很不安全,PATH 应该被转义。我不知道 Python 的等效方法,但 PHP 开发人员会使用 Popen("/usr/bin/file --mime ".escapeshellarg(PATH)); 举个例子,你的代码将无法处理包含换行符或引号的文件,可能还有 $ 美元符号,但它也会保护你免受黑客进行类似于 PATH='; rm -rfv / 的 Shell 注入攻击 - hanshenrik

7

@toivotuo的方法在python3下对我来说是最好和最可靠的。我的目标是识别没有可靠.gz扩展名的gzipped文件。我安装了python3-magic。

import magic

filename = "./datasets/test"

def file_mime_type(filename):
    m = magic.open(magic.MAGIC_MIME)
    m.load()
    return(m.file(filename))

print(file_mime_type(filename))

对于gzip压缩文件,它会返回:application/gzip; charset=binary
对于未压缩的文本文件(iostat数据):text/plain; charset=us-ascii
对于tar文件:application/x-tar; charset=binary
对于bz2文件:application/x-bzip2; charset=binary
最后但并非最不重要的是.zip文件:application/zip; charset=binary

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