Word 套版實戰 - 擁抱 ASP.NET Core/.NET 5,前進 Linux
2 |
昨天提到 OpenXML SDK Word 套版技巧,我已實際應用過好一陣子,但都是用在 .NET Framework 4.6 / ASP.NET MVC 專案。這套做法能不能搬進 ASP.NET Core,甚至丟到 Linux / Docker 主機執行呢?
當然沒問題!
OpenXML SDK 支援 .NET 3.5/4.0/4.6+、.NET Standard 1.3、.NET Standard 2.0 參考,意味原本在 Windows + .NET Framework 4.6+ 執行的 OpenXML 相關程式,改參照 .NET Standard 2.0 版便可搬進 .NET Core 3.1/.NET 5 跨平台執行。
不過,轉移過程我踢到小石頭。以下是重現問題的程式範例:
public static byte[] CreateNew()
{
using (var ms = new MemoryStream())
{
using (var docx = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document))
{
var mainPart = docx.AddMainDocumentPart();
mainPart.Document = new Document();
mainPart.Document.AppendChild(new Body());
docx.MainDocumentPart.Document.Body.Append(new Paragraph(new Run(new Text("Hello"))));
docx.Save();
return ms.ToArray();
}
}
}
用 MemoryStream 儲存 docx 二進位內容是我在 .NET Framework 慣用多時的程式寫法,如以上範例用 MemoryStream 建立新文件並加入一段文字,呼叫 WordprocessingDocument.Save() 後傳回 MemoryStream.ToArray() 即可取得 docx 的 byte[] 內容回應網頁要求或存成檔案,好處是過程不產生任何暫存檔。這個寫法用在許多 .NET Framework 專案,向來執行正常,但搬進 .NET 5,新文件有產生但不包含新增的文字。用 MemoryStream 進行套版作業時也有類似問題,Line-By-Line 偵錯確認置換動作均已執行,但 MemoryStream.ToArray() 傳回的 byte[] 卻是未經修改的原始範本檔,問題同樣只出現在 .NET 5,在 .NET Framework 裡不會發生。
卡住了近半小時,終於找到關鍵,將 return ms.ToArray() 移到 using (var docx = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document)) 之後,問題即告消失。
using (var ms = new MemoryStream())
{
using (var docx = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document))
{
var mainPart = docx.AddMainDocumentPart();
mainPart.Document = new Document();
mainPart.Document.AppendChild(new Body());
docx.MainDocumentPart.Document.Body.Append(new Paragraph(new Run(new Text("Hello"))));
docx.Save();
}
return ms.ToArray(); //移至 using WordprocessingDocument 結束後
}
原始碼專案提到 .NET Core/.NET 5 版有個已知問題 - On .NET Core and .NET 5, zip packages do not have a way to stream data.,我猜想是基於類似限制,有些更新在 WordprocessingDocument.Dispose() 後才會反映到 Stream。
排除問題後,我成功在 CentOS 跑 .NET 程式產生 Word 文件,意味著可以放心在 ASP.NET Core 用 OpenXML 處理 Word 套版,不必擔心平台限制! (灑花)
An issue while migrating OpenXML SDK Word processing to .NET Core/.NET 5.
Comments
# by solonglin
不曉得一開始直接用c# 8.0 的新語法 ,是不是就不會踢到小石頭,就是 using var ms = new MemoryStream(); using var docx = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document);
# by Anthony
to solonglin: 等於"重現問題的程式範例", 所以...(腳很痛) -> google "using var ms = new MemoryStream(); c# 8.0" to Jeffrey: docx.Save(); 轉成 docx.Close(); 能否解決問題?