2017-05-25 22 views
2

我經常收到帶有必須解壓縮並保存到磁盤的附件的電子郵件。我基本上做以下(在Python 2.7):壓縮的附件中斷email.message.Message.get_payload()

message = email.message_from_file(sys.stdin) 
for part in message.walk(): 
    path = email.header.decode_header(part.get_filename())[0][0] 
    content = part.get_payload(decode=True) 
    with open(path, 'w') as f: 
     f.write(content) 

這種方法已經爲所有類型的附件和我到目前爲止已經收到除非該附件是一個ZIP文件和Content-Transfer-EncodingContent-Transfer-Encoding所有口味是'引用 - 可打印的'。在這種情況下是被寫入ZIP文件比原來少了一個字節(約的方式60-80%通過文件),並unzip報告這樣的錯誤:

% unzip -l foo.zip 
Archive: foo.zip 
error [foo.zip]: missing 1 bytes in zipfile 
    (attempting to process anyway) 
    Length  Date Time Name 
--------- ---------- ----- ---- 
    440228 01-00-1980 00:00 foo - bar.csv 
---------      ------- 
    440228      1 file 

% unzip foo.zip 
Archive: foo.zip 
error [foo.zip]: missing 1 bytes in zipfile 
    (attempting to process anyway) 
error [foo.zip]: attempt to seek before beginning of zipfile 
    (please check that you have transferred or created the zipfile in the 
    appropriate BINARY mode and that you have compiled UnZip properly) 
    (attempting to re-compensate) 
    inflating: foo - bar.csv bad CRC 4c86de66 (should be a53f73b1) 

解壓後的結果與原始CSV大小相差約0.01%,最後20-40%左右的文件出現亂碼。現在


,該代碼處理附於「的base64」就好了ZIP文件,它處理連接爲「引用可打印」就好了其他內容(Excel文件,CSV文件)。我知道ZIP附件的內容是無損的足夠,我的普通電子郵件閱讀器可以將其保存到磁盤就好,並提取完美的原始內容。 (真的電子郵件閱讀器在保存我的Python沒有做的附件時可能會執行一些錯誤更正嗎?)

是否有一個已知的問題,Python無法讀取以引用打印方式發送的ZIP文件?有沒有其他函數來自Python的email包我可以嘗試正確解密這個內容?

+0

請勿使用_ ** quoted-printable ** _,請改用'base-64'。 – stovfl

+0

@stovfl謝謝,但我是電子郵件的收件人,而不是發件人。 – dg99

+0

Zip文件是'binary',你不能從** quoted-printable **解碼。要求發件人更改爲'base64'。 – stovfl

回答

3

這種情況下的問題是發件人的二進制附件(ZIP文件)寫得不好,因此它們包含\r\n序列。也就是說,ZIP格式的文件本身(不是被壓縮的文件)包含偶爾的CRLF對。我無法推測這些是如何進入ZIP輸出的;我不認爲任何商業或開源的拉鍊將包括CRLF在其輸出...

根據第4條的quoted-printable encoding#,在原有的「文本」換行符(在這種情況下, ZIP附件)必須在編碼中表示爲裸\r\n(然後解碼,然而解碼器的區域設置指示)。顯然,當換行符的確切形式有意義時(例如,當它本身是編碼時),這是非常糟糕的。和RFC甚至約含文字行的二進制數據的古怪評論突破:

由於非文本類型的規範表示一般不包括換行符的表示,沒有硬換行(即行打破了意圖是有意義的並且被顯示給用戶)應該出現在這種類型的引用可打印編碼中。

所以這是在RFC的最後一個巨大的警示:

警告對實現程序:如果二進制數據以引用可打印編碼,必須小心編碼CR和LF字符「 = 0D「和」= 0A「。特別是,二進制數據中的CRLF序列應編碼爲「= 0D = 0A」。否則,如果CRLF表示爲強硬換行符,則可能會在具有不同換行符約定的平臺上錯誤地解碼。

發送者顯然是不服從發件人之間時,編碼此警告,等等一些郵件傳輸代理或網關和我的決定,對我的語言環境適當的換行符僅僅是\n(這通常是)。

無論如何,我發現這是通過比較我的quopri-解碼的附件字節與附加的ZIP文件的原始副本的問題。除了每個CRLF在原來只是一個LF在我的解碼。因爲\r顯然是有意義的,並且因爲QP編碼中的每一個換行符都正確地以換行符=字符開頭,所以我簡單地爲來自此發送者的所有QP編碼的application MIME類型編寫了以下轉換:

if part['Content-Disposition'].startswith('attachment') and \ 
    part['Content-Transfer-Encoding'] == 'quoted-printable': 
    rawContent = part.get_payload(decode=False) 
    fixedRawContent = re.sub(r'([^=])\n', r'\1=0D=0A=\n', rawContent) 
    decodedContent = quopri.decodestring(fixedRawContent) 

通過把每個硬(意外)換行到編碼\r\n(後面我自己的軟換行,這樣我就不必擔心產生任何超長線),解碼功能盡職盡責地說\r\n成ZIP ,然後正確提取。