2013-07-05 53 views
0

在我的web應用程序(Flask)中,我讓用戶上傳word文檔。確認上傳的文件是Python中的word文檔

我檢查文件的擴展名是.doc.docx。 但是,我將.jpg文件的擴展名更改爲.docx,並且它也通過了(如我所料)。

有沒有辦法來驗證上傳的文件確實是一個word文檔?我搜索並閱讀了關於文件頭部的內容,但找不到任何其他信息。

我使用boto將文件上傳到aws,萬一它很重要。 謝謝。

+1

這可能幫助: http://stackoverflow.com/questions/10937350/how-to-check-type-of-files-without-extensions-in-python –

+1

這可能幫助: http://www.garykessler.net/library/file_sigs.html –

+0

真棒謝謝你們 – Kreutzer

回答

1

Docx文件實際上是zip文件。該郵編包含三個基本文件夾:worddocProps_rels。因此,使用zipfile來測試這個文件中是否存在這些文件。改編自Check if a directory exists in a zip file with Python

但是,包含這些文件夾將繞過驗證任何ZIP

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") 

代碼。 我也不知道它是否適用於DOC或加密的DOCS。

1

那麼,python-magic圖書館在評論鏈接的問題看起來像一個非常簡單的解決方案。

不過,我會給出更多的手動選項。根據this site,DOC文件具有D0 CF 11 E0 A1 B1 1A E1(8字節)的簽名,而DOCX文件具有50 4B 03 04(4字節)。假設這些文件是微軟的,因爲它們來自微軟(儘管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 

所以,在這裏我們解壓從緩衝器包含從文件中讀取的二進制數據中的第一小端(「<‘)字節(’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字節,並與其他指紋進行比較。

+0

請注意,你可能只能直接閱讀'buf [:8]'。我承認無知在所有系統上它的行爲是否相同。 'struct.unpack_from'方法保證工作原理相同。 – 2013-07-05 22:39:28

+0

非常感謝您的詳細解答,我會試一試 – Kreutzer

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 

您可以試試這個。

0

您可以使用Python,DOCX庫

下面的代碼將引發值誤差在文件不是的docx文件。

from docx import Document 
try: 
    Document("abc.docx") 
except ValueError: 
    print "Not a valid document type"