在我的网络应用程序(Flask)中,我让用户上传word文档。
我检查文件的扩展名是.doc
还是.docx
。
但是,我将.jpg
文件的扩展名更改为.docx
,并且它也已经过了(正如我预期的那样)。
有没有办法验证上传的文件确实是word文档?我搜索并阅读了一些关于文件标题的内容,但找不到任何其他信息。
我使用boto将文件上传到aws,以防万一。 感谢。
答案 0 :(得分:1)
Docx文件实际上是zip文件。此zip包含三个基本文件夹:word
,docProps
和_rels
。因此,使用zipfile
来测试此文件中是否存在这些文件。
import zipfile
def isdir(z, name):
return any(x.startswith("%s/" % name.rstrip("/")) for x in z.namelist())
def isValidDocx(filename):
f = zipfile.ZipFile(filename, "r")
return isdir(f, "word") and isdir(f, "docProps") and isdir(f, "_rels")
代码改编自Check if a directory exists in a zip file with Python
但是,包含这些文件夹的任何ZIP都将绕过验证。 我也不知道它是否适用于DOC或加密的DOCS。
答案 1 :(得分:1)
嗯,评论中链接的问题中的python-magic
库看起来像是一个非常直接的解决方案。
尽管如此,我还是会提供更多手动选项。根据{{3}},DOC文件的签名为D0 CF 11 E0 A1 B1 1A E1
(8个字节),而DOCX文件的签名为50 4B 03 04
(4个字节)。两者都有0的偏移。可以安全地假设这些文件是小端的,因为它们来自微软(不过,可能Office文件在Mac上是Big Endian?我不确定)
您可以使用struct
模块解压缩二进制数据,如下所示:
>>> with open("foo.doc", "rb") as h:
... buf = h.read()
>>> byte = struct.unpack_from("<B", buf, 0)[0]
>>> print("{0:x}".format(byte))
d0
所以,这里我们从包含从文件读取的二进制数据的缓冲区中解压缩第一个小端(“&lt;”)字节(“B”),偏移量为0,我们发现“D0”, doc文件中的第一个字节。如果我们将偏移量设置为1,我们得到CF,即第二个字节。
让我们检查它是否确实是一个DOC文件:
def is_doc(file):
with open(file, 'rb') as h:
buf = h.read()
fingerprint = []
if len(buf) > 8:
for i in range(8):
byte = struct.unpack_from("<B", buf, i)[0]
fingerprint.append("{0:x}".format(byte))
if ' '.join(fingerprint).upper() == "D0 CF 11 E0 A1 B1 1A E1":
return True
return False
>>> is_doc("foo.doc")
True
不幸的是我没有任何要测试的DOCX文件但过程应该是相同的,除了你只获得前4个字节并与其他指纹进行比较。
答案 2 :(得分:0)
如果安装外部库是一个选项,那么有一个Python的docx库。您可以尝试使用它:https://github.com/mikemaccana/python-docx
答案 3 :(得分:0)
我使用python-magic
来验证文件类型是否是word文档。
但是我遇到了很多问题。如:不同的单词版本或不同的软件导致不同的类型。所以我放弃了python-magic
。
这是我的解决方案。
DOC_MAGIC_BYTES = [
"D0 CF 11 E0 A1 B1 1A E1",
"0D 44 4F 43",
"CF 11 E0 A1 B1 1A E1 00",
"DB A5 2D 00",
"EC A5 C1 00"
]
DOCX_MAGIC_BYTES = [
"50 4B 03 04"
]
def validate_is_word(content):
magic_bytes = content[:8]
fingerprint = []
bytes_len = len(magic_bytes)
if bytes_len >= 4:
for i in xrange(bytes_len):
byte = struct.unpack_from("<B", magic_bytes, i)[0]
fingerprint.append("{:02x}".format(byte))
if not fingerprint:
return False
if is_docx_file(fingerprint):
return True
if is_doc_file(fingerprint):
return True
return False
def is_doc_file(magic_bytes):
four_bytes = " ".join(magic_bytes[:4]).upper()
all_bytes = " ".join(magic_bytes).upper()
return four_bytes in DOC_MAGIC_BYTES or all_bytes in DOC_MAGIC_BYTES
def is_docx_file(magic_bytes):
type_ = " ".join(magic_bytes[:4]).upper()
return type_ in DOCX_MAGIC_BYTES
你可以试试这个。
答案 4 :(得分:0)
您可以使用python-docx库
以下代码将引发值错误,该文件不是docx文件。
from docx import Document
try:
Document("abc.docx")
except ValueError:
print "Not a valid document type"
答案 5 :(得分:0)
我使用文件类型python lib检查并比较mime类型及其文档扩展名,因此我的用户不能仅仅通过更改其文件扩展名来愚弄我。
pip install filetype
然后
import filetype
kind = filetype.guess('path/to/file')
mime = kind.mime
ext = kind.extension
您可以检查他们的文档here
答案 6 :(得分:0)
python-magic
在检测 docx
和 pptx
格式方面做得非常好。
这里有几个例子:
In [60]: magic.from_file("oz123.docx")
Out[60]: 'Microsoft Word 2007+'
In [61]: magic.from_file("oz123.docx", mime=True)
Out[61]: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
In [62]: magic.from_file("presentation.pptx")
Out[62]: 'Microsoft PowerPoint 2007+'
In [63]: magic.from_file("presentation.pptx", mime=True)
Out[63]: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
由于 OP 询问文件上传,文件句柄不是很有用。幸运的是,
magic
支持从缓冲区检测:
In [63]: fdox
Out[63]: <_io.BufferedReader name='/home/oz123/Documents/oz123.docx'>
In [64]: magic.from_buffer(fdox.read(2048))
Out[64]: 'Zip archive data, at least v2.0 to extract
天真地,我们读取的数量太小......读取更多字节可以解决问题:
In [65]: fdox.seek(0)
Out[65]: 0
In [66]: magic.from_buffer(fdox.read(4096))
Out[66]: 'Microsoft Word 2007+'
In [67]: fdox.seek(0)
Out[67]: 0
In [68]: magic.from_buffer(fdox.read(4096), mime=True)
Out[68]: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'