如何在Python中从文件缓冲区检测MIME类型(尤其是.xls、.doc、.ppt等旧版Office格式)
我最近在开发一个通用的Python文本提取库,这个库得支持两种输入场景:要么是本地文件的路径字符串,要么是像BytesIO这类类文件对象——比如网页上传的文件流,或者内存里生成的临时文件。要给不同类型的文件匹配对应的提取逻辑,第一步就得准确检测出文件的MIME类型,特别是.xls、.doc、.ppt这些旧版Office格式,踩了不少坑,现在把靠谱的实现方法分享出来。
我目前的基础方案:用python-magic
我现在用的是python-magic库,它封装了系统的libmagic工具,核心靠读取文件头的特征字节来判断MIME类型,比单纯靠文件扩展名靠谱多了——毕竟扩展名是可以随便改的。不过不同系统安装时要注意细节:
- Linux(Debian/Ubuntu系):得先装系统依赖,执行
sudo apt-get install libmagic1 - macOS:用Homebrew安装
brew install libmagic - Windows:直接装预编译的
python-magic-bin就行,不用额外装系统库
针对不同输入类型的具体实现
1. 处理本地文件路径
这种情况最简单,直接把路径传给python-magic就行:
import magic def get_mime_type_from_path(file_path): # 初始化时指定mime=True,直接返回MIME类型字符串 mime_detector = magic.Magic(mime=True) return mime_detector.from_file(file_path)
比如给一个旧版.doc文件的路径,会返回application/msword,识别得很准确。
2. 处理类文件对象(比如BytesIO)
这时候不能用from_file方法了,得先读取文件的开头字节——MIME类型的标识都存在文件头里,一般读前1024字节就足够覆盖所有常见格式,包括旧版Office的特征字节。重点是读完后要把文件指针移回开头,不然后续的文本提取操作会从中间开始读,肯定出问题:
import magic from io import BytesIO def get_mime_type_from_fileobj(file_obj): # 重置指针到文件开头,避免之前的读取操作干扰 file_obj.seek(0) # 读取前1024字节的文件头 header_bytes = file_obj.read(1024) # 把指针移回去,不影响后续的提取逻辑 file_obj.seek(0) mime_detector = magic.Magic(mime=True) return mime_detector.from_buffer(header_bytes)
用这个方法处理BytesIO里的旧版.xls文件,会返回application/vnd.ms-excel,完全没问题。
3. 无依赖备选方案:手动检测旧版Office格式
如果不想依赖python-magic和libmagic,也可以手动匹配旧版Office的特征——所有旧版Office格式(.doc、.xls、.ppt)的前8字节都是固定的十六进制D0 CF 11 E0 A1 B1 1A E1,转成字节串就是b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1'。可以写个简单的判断函数:
def is_old_office_format(file_obj): file_obj.seek(0) # 读取前8字节的特征码 header = file_obj.read(8) file_obj.seek(0) return header == b'\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1'
如果返回True,就可以确定是旧版Office文件,要是需要细分具体是.doc、.xls还是.ppt,可以再结合后续的字节特征或者文件名扩展名辅助判断。
踩坑提醒
- 处理类文件对象时一定要重置文件指针!我之前因为忘了这一步,导致检测时读的是空字节,返回了错误的MIME类型,排查了好久才找到问题。
- 旧版Office和新版OOXML格式(.docx、.xlsx)的文件头完全不同,新版是ZIP文件头,检测时别搞混了。
备注:内容来源于stack exchange,提问作者GBBL

