.msg 檔是什麼?格式解析
從技術角度解析 Outlook 的 .msg 格式:OLE2 容器、MAPI 屬性資料流、內文的儲存方式,以及為什麼其他軟體讀不了它。
.msg 是 Microsoft Outlook 用來把單一項目存成獨立檔案的格式。雖然名字 叫 msg,它裝的不只是郵件 —— 從 Outlook 存出的約會、聯絡人、工作、記事與會議邀請, 產生的都是 .msg 檔。
外層容器:Compound File Binary Format
.msg 本質上是一個 OLE2 複合文件 —— 等於在單一檔案 裡塞了一套小型檔案系統,有目錄(storage)也有檔案(stream)。這類檔案開頭一定 是同樣的八個簽章位元組 D0 CF 11 E0 A1 B1 1A E1,因此就算副檔名被改過, 解析器依然認得出來。
舊版的 .doc、.xls、.ppt 用的是同一種容器, 差別在於內部資料流的命名與意義 —— .msg 的部分由 [MS-OXMSG] 規格定義。
裡面存了什麼
每個郵件屬性各自存成一個資料流,名稱就是 MAPI 屬性標籤(16 位元屬性編號 + 16 位元 型別)。例如:
| 屬性 | 標籤 | 內容 |
|---|---|---|
| PidTagSubject | 0037001F | 主旨 |
| PidTagBody | 1000001F | 純文字內文 |
| PidTagHtml | 10130102 | HTML 內文(原始位元組) |
| PidTagRtfCompressed | 10090102 | 壓縮過的 RTF 內文 |
| PidTagSenderEmailAddress | 0C1F001F | 寄件者位址 |
收件者與附件則不是屬性,而是子儲存區 —— 例如 __recip_version1.0_#00000000 與 __attach_version1.0_#00000000 —— 各自內部再放自己的屬性資料流。
內文的三種存法
這正是多數簡易檢視器出錯的地方,也是同一個檔案在某個工具裡完美、在另一個工具裡 卻空白的原因。Outlook 可能把內文存成:
- 純文字,放在
PidTagBody。 - HTML,放在
PidTagHtml,是必須依PidTagInternetCodepage指定的字碼頁去解碼的原始位元組。 一律當成 UTF-8 解碼,正是中文、日文、韓文、西里爾文與希臘文郵件變成亂碼的主因。 - 壓縮 RTF,放在
PidTagRtfCompressed。當原始郵件 是 HTML 時,Outlook 會依 [MS-OXRTFEX] 規格把 HTML 封裝進 RTF 裡。要還原就得先解壓縮,再處理\htmltag與\htmlrtf控制字進行反封裝。跳過這一步的檢視器,在相當比例的真實 Outlook 郵件上都會顯示 空白內文。
內嵌圖片與 cid: 參照
出現在內文中的圖片,其實是帶有 PidTagAttachContentId 的一般附件, HTML 內文再以 <img src="cid:image001.png@01D9…"> 參照它們。 檢視器必須把每個 cid: 對應回附件並換成可用的網址,否則簽名檔與 公司 logo 的位置就只會剩下破圖圖示。
手動讀取那些資料流
每一個屬性都存在一個名字本身就編碼了「它是什麼」的資料流裡:__substg1.0_XXXXYYYY,其中 XXXX 是十六進位的屬性標籤,YYYY 是它的型別。所以主旨(標籤 0x0037)以 Unicode (0x001F)儲存時,會出現為 __substg1.0_0037001F。 同一個標籤結尾是 001E 的話,就是同一個欄位的八位元版本。
這套命名正是「不用 Outlook 也讀得到 .msg」的全部原因。 只要有一個 OLE2 函式庫,你就能列舉資料流、查表對照標籤, 檔案會自己交出結構,過程中不需要任何微軟的程式碼。
還有兩個慣例很重要。收件者不是存在單一資料流裡的清單 —— 每一位收件者是一個獨立的子儲存區,叫做__recip_version1.0_#00000000、#00000001 以此類推, 各自帶著自己的一整組屬性流。附件用同樣的模式,放在__attach_version1.0_#... 底下。而固定長度的屬性 —— 布林值、整數、時間戳 —— 根本不是資料流,它們被打包在__properties_version1.0 這張表裡。
字元編碼,以及它是怎麼壞掉的
以 001F 儲存的屬性是 UTF-16LE,沒有歧義。 以 001E 儲存的則是某個 code page 下的位元組,而檔案必須告訴你是哪一個:PidTagInternetCodepage(0x3FDE), 或退而求其次的 PidTagMessageCodepage。
忽略這些、直接假設 UTF-8 的讀取器,會弄壞每一封用繁體中文、日文、韓文、 西里爾字母或希臘文寫的郵件 —— 這也是「郵件打得開,但文字全是替代字元」 最常見的解釋。那封郵件沒有損毀,只是被用錯的對照表解碼了。
「這是一個 OLE2 檔案」的實際後果
外層容器是複合檔案這件事有一個值得知道的後果:它是一個小型檔案系統, 有磁區配置表、目錄樹和可用空間。用文字編輯器編輯 .msg不只是把某些文字弄亂 —— 它會**破壞磁區鏈**,而檔案會變成任何工具都救不回來的狀態。 真的需要檢查一個檔案時,請對副本動手。
這也表示同一封郵件存成 .msg 通常比存成 .eml 大, 有時大得多。它把內文存了不只一次 —— HTML、RTF、純文字都有 —— 再加上一堆傳輸格式裡根本沒有位置放的 MAPI 記帳資料。
為什麼其他軟體不支援
微軟雖然公開了規格,卻從未把它推為交換標準;而且格式中編入了大量 Exchange 專屬 概念 —— legacy DN、投票按鈕、委派資訊、訊息類別 —— 在標準網際網路郵件中根本沒有 對應物。要支援它就等於實作一份微軟規格,對競爭者自家格式毫無好處,所以幾乎沒人做。
標準的替代方案是純文字的 .eml。可以 比較兩種格式,看看轉檔會得到什麼、失去什麼。