| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| Open XML SDK (Microsoft 官方) | 無需安裝 Office、效能高、支援 .docx 所有功能、免費開源 | API 底層較繁瑣、需手動組裝 XML 結構、學習曲線陡 | 伺服器端批量生成、高併發、不想付費 |
| Aspose.Words (商業庫) | API 極簡單 (類 Word 物件模型)、支援郵件合併、轉 PDF 完美、相容性極強 | **收費授權較貴**、DLL 體積大 | 企業級專案、複雜報表、預算充足、求開發效率 |
| COM Interop (Word Automation) | 程式碼直觀 (VBA 風格)、功能最全 | **嚴禁用於伺服器端** (Microsoft 官方不支援)、需安裝 Office、併發極差、易卡死 | 僅限桌面端單機工具 |
| NPOI (開源) | 免費、支援 .doc/.xls (舊格式) | .docx (OOXML) 支援較弱、Word 功能不如 Open XML SDK 完整 | 維護舊系統 .doc 格式、簡單表格導出 |
Install-Package DocumentFormat.OpenXml
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Data;
using System.IO;
public byte[] GenerateWordReport(DataTable dtMain, List dtTables, List images) {
using (MemoryStream ms = new MemoryStream()) {
using (WordprocessingDocument doc = WordprocessingDocument.Create(ms, WordprocessingDocumentType.Document)) {
MainDocumentPart mainPart = doc.AddMainDocumentPart();
mainPart.Document = new Document();
Body body = mainPart.Document.AppendChild(new Body());
// --- 樣式預定義 ---
// 標題樣式:置中、字體、大小
ParagraphProperties titleProps = new ParagraphProperties(
new Justification() { Val = JustificationValues.Center },
new SpacingBetweenLines() { After = "200" }
);
RunProperties titleRunProps = new RunProperties(
new RunFonts() { Ascii = "微軟雅黑", HighAnsi = "微軟雅黑" },
new FontSize() { Val = "32" }, // 16pt = 32 half-points
new Bold()
);
// 1. 主標題
AppendParagraph(body, "數據分析報告", titleProps, titleRunProps);
// 2. 多級列表 (一、 1、 (1) 結構)
// Open XML 需定義 NumberingDefinitionsPart,此處簡化為手動添加編號文字
AppendNumberedItem(body, "一、內容1", 1, "微軟雅黑", "24");
AppendNumberedItem(body, "1、*******", 2, "微軟雅黑", "22");
AppendNumberedItem(body, "2、*****", 2, "微軟雅黑", "22");
AppendNumberedItem(body, "二、內容二", 1, "微軟雅黑", "24");
AppendNumberedItem(body, "(1)*****", 3, "微軟雅黑", "22");
// 3. 動態表格 (來自 DataTable)
if (dtTables != null && dtTables.Count > 0) {
AppendParagraph(body, "三、數據表格", titleProps, titleRunProps); // 子標題
foreach (var table in dtTables) {
AppendDataTable(body, table);
}
}
// 4. 圖片插入
if (images != null) {
foreach (var imgBytes in images) {
AppendImage(body, mainPart, imgBytes);
}
}
mainPart.Document.Save();
}
return ms.ToArray();
}
}
// 輔助方法:追加段落
void AppendParagraph(Body body, string text, ParagraphProperties pPr, RunProperties rPr) {
Paragraph p = body.AppendChild(new Paragraph(pPr));
Run r = p.AppendChild(new Run(rPr));
r.AppendChild(new Text(text) { Space = SpaceProcessingModeValues.Preserve });
}
// 輔助方法:追加表格 (核心邏輯)
void AppendDataTable(Body body, DataTable dt) {
Table table = new Table();
// 表格屬性:邊框、置中、自動調整
table.AppendChild(new TableProperties(
new TableBorders(
new TopBorder() { Val = BorderValues.Single, Size = 4 },
new BottomBorder() { Val = BorderValues.Single, Size = 4 },
new LeftBorder() { Val = BorderValues.Single, Size = 4 },
new RightBorder() { Val = BorderValues.Single, Size = 4 },
new InsideHorizontalBorder() { Val = BorderValues.Single, Size = 4 },
new InsideVerticalBorder() { Val = BorderValues.Single, Size = 4 }
),
new TableJustification() { Val = TableRowAlignmentValues.Center },
new TableLayout() { Type = TableLayoutValues.Fixed }
));
// 表頭
TableRow headerRow = new TableRow();
foreach (DataColumn col in dt.Columns) {
TableCell cell = new TableCell(new Paragraph(new Run(new Text(col.ColumnName))));
// 可加粗表頭
headerRow.Append(cell);
}
table.Append(headerRow);
// 數據行
foreach (DataRow row in dt.Rows) {
TableRow dataRow = new TableRow();
foreach (var item in row.ItemArray) {
dataRow.Append(new TableCell(new Paragraph(new Run(new Text(item.ToString())))));
}
table.Append(dataRow);
}
body.Append(table);
}
// 輔助方法:插入圖片
void AppendImage(Body body, MainDocumentPart mainPart, byte[] imgBytes) {
ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Jpeg); // 或 Png
using (MemoryStream imgStream = new MemoryStream(imgBytes)) {
imagePart.FeedData(imgStream);
}
string relId = mainPart.GetIdOfPart(imagePart);
var element = new Drawing(
new DW.Inline(
new DW.Extent() { Cx = 990000L, Cy = 792000L }, // 大小 EMU
new DW.EffectExtent() { LeftEdge = 0L, TopEdge = 0L, RightEdge = 0L, BottomEdge = 0L },
new DW.DocProperties() { Id = (UInt32Value)1U, Name = "Picture" },
new DW.NonVisualGraphicFrameDrawingProperties(
new A.GraphicFrameLocks() { NoChangeAspect = true }),
new A.Graphic(
new A.GraphicData(
new PIC.Picture(
new PIC.NonVisualPictureProperties(
new PIC.NonVisualDrawingProperties() { Id = 0U, Name = "Image" },
new PIC.NonVisualPictureDrawingProperties()),
new PIC.BlipFill(
new A.Blip() { Embed = relId, CompressionState = A.BlipCompressionValues.Print },
new A.Stretch(new A.FillRectangle())),
new PIC.ShapeProperties(
new A.Transform2D(
new A.Offset() { X = 0L, Y = 0L },
new A.Extents() { Cx = 990000L, Cy = 792000L }),
new A.PresetGeometry(new A.AdjustValueList()) { Preset = A.ShapeTypeValues.Rectangle }))
) { Uri = "https://schemas.openxmlformats.org/drawingml/2006/picture" })
) { DistanceFromTop = 0, DistanceFromBottom = 0, DistanceFromLeft = 0, DistanceFromRight = 0 });
Paragraph p = body.AppendChild(new Paragraph(new ParagraphProperties(new Justification() { Val = JustificationValues.Center })));
Run r = p.AppendChild(new Run());
r.Append(element);
}
public void DownloadWord() {
byte[] fileBytes = GenerateWordReport(...); // 獲取數據
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.AddHeader("Content-Disposition", "attachment;filename=Report_" + DateTime.Now.ToString("yyyyMMdd") + ".docx");
Response.BinaryWrite(fileBytes);
Response.End();
}
**不建議使用 COM 元件 (Word Automation) 在 ASP.NET 中**,極易導致 IIS 僵死、記憶體洩漏、權限問題。
**首選 Open XML SDK**:免費、輕量、官方標準、適合伺服器端高併發。
**預算允許且追求極致開發速度/複雜排版**:Aspose.Words 是最佳商業選擇。
上述代碼展示了 Open XML SDK 處理「標題居中、多級列表、動態表格、圖片」的核心邏輯,可直接擴展應用於實際專案。