Quantcast
Viewing all articles
Browse latest Browse all 2480

【茶包射手日記】EPPlus 3.1.2 Bug Fixing

發現EPPlus 3.1.2版Bug一枚。

開啟現有xlsx後,不做任何修改就儲存,再使用Excel開啟會出錯。例如以下範例:

using (ExcelPackage p = new ExcelPackage(new FileInfo("通訊錄.xlsx")))

    p.Save();
}

程式執行後使用Excel開啟重新儲存的通訊錄.xlsx檔案,會出現以下錯誤:

Excel 在 '通訊錄.xlsx' 中找到無法讀取的內容。您要回復此活頁簿的內容嗎? 若您信任此活頁簿的來源,請按一下[是]。
Excel found unreadable content in filename.xls. Do you want to recover the contents of this workbook? If you trust the source of this workbook, click Yes.

試圖修復,Excel會回報進一步訊息。

Image may be NSFW.
Clik here to view.

檢視錯誤訊息xml檔,會發現關於style.xml及sheet*.xml的修復報告:

error067160_07.xml檔案 'X:\TestEPPlus\bin\Debug\通訊錄.xlsx' 中偵測出錯誤
已移除的記錄: /xl/styles.xml 部分的 樣式 (樣式)
已修復的記錄: /xl/worksheets/sheet1.xml 部分的 儲存格資訊
已修復的記錄: /xl/worksheets/sheet1.xml 部分的 欄資訊

修復後的xlsx檔案,資料完好,但格式設定大亂。

幸好,xlsx檔的本質是個ZIP壓縮檔,解開後就可以進一步探查真相。在xl\styles.xml檔案中,我發現幾處不合理的XmlNode:

<cellStyleXfs count="2">
  <xf numFmtId="0" fontId="0" fillId="0" borderId="0">
    <alignment vertical="center" />
  </xf>
</cellStyleXfs>
<cellXfs count="3">
  <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="1">
    <alignment vertical="center" />
  </xf>
  <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="1">
    <alignment vertical="center" />
  </xf>
</cellXfs>
<cellStyles count="2">
  <cellStyle name="一般" xfId="1" builtinId="0" />
</cellStyles>

cellStyleXfs count=2,但資料只有一筆、cellXfs count=3,但資料只有兩筆,而且xfId都是"1",cellStyles count=2但資料只有一筆。推測就是這些錯亂導致styles.xml出錯,而sheet1.xml與sheet2.xml的對應也因此損壞,造成格式顯示異常。

感謝老天爺,EPPlus是個Open Source Project,下載原始碼用Line-By-Line追進元件內部,沒多久就找到Bug來源:

EPPlus\ExcelStyles.cs Line 562 UpdateXml() method:

Line 561:            //NamedStyles
Line 562:            count = 1; <== 應改成count = 0;才合理

經過修改,原本存檔會弄壞xlsx檔的問題消失了! 順便把我的發現回報到CodePlex,終於有機會為Open Source社群略盡薄棉之力,Open Source萬歲!

Image may be NSFW.
Clik here to view.
Image may be NSFW.
Clik here to view.

Viewing all articles
Browse latest Browse all 2480

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>