昨天提到 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(); 能否解決問題?

Post a comment