1. 程式人生 > >.NET Core使用NPOI匯出複雜Word詳解

.NET Core使用NPOI匯出複雜Word詳解

前言:

  最近使用NPOI做了個匯出Word文件的功能,關於使用.NET Core 匯出Word文件的方式有很多。最終我為什麼選擇了NPOI來實現了這個功能,首先是NPOI是一個開源,免費且容易上手的第三方框架(並且現在已支援.NET Core,GitHub原始碼地址:https://github.com/tonyqus/npoi)。因為之前使用NPOI匯出Execl比較多,這次第一次使用NPOI 來匯出Word文件還真沒有什麼頭緒。首先看了下GItHub中的原始碼有一個簡單Word匯出的示例,然後在看了網上有很多關於NPOI匯出Word文件的案例,發現一個特點網上的好像都差不多,對於我而言網上的這些案例完全能夠實現我的這個功能,但是感覺看了網上這些案例對NPOI例項化段落,表格和設定相關樣式不太清楚(可能是因為自己笨),並且假如使用網上的方法來實現我的功能的話程式碼量會比較大,而且感覺程式碼非常的冗餘(我是一個追求程式碼簡潔的人,怎麼能夠容忍這樣的事情發生呢!),因此通過查閱了一些資料和自己的理解,把關於使用NPOI匯出Word時所要涉及的一些段落,表格樣式做了相關注釋,和把段落和表格的建立例項,設定文字、字型、對齊方式都封裝了起了(為了少寫程式碼),文章末尾會附上一個完整的案例下載地址。

 一、首先引入NPOI NuGet:

版本說明:

  NPOI 2.4.1 (注意不同版本可能使用的姿勢有點小差別)

程式包管理器控制檯輸入一下命令安裝:

Install-Package NPOI -Version 2.4.1

通過NuGet管理解決方案安裝:

二、匯出的Work文件內容格式樣式:

 

 

 

 

 三、NPOI中的XWPFRun文字物件建立和屬性簡單概述:

XWPFRun文字物件說明:

  XWPFRun是段落的文字物件,先建立段落物件才能夠在段落物件的基礎上建立文件物件,並設定相關文字樣式。

如下所示:

        /// <summary>
        /// 建立word文件中的段落物件和設定段落文字的基本樣式(字型大小,字型,字型顏色,字型對齊位置)
        /// </summary>
        /// <param name="document">doc文件物件</param>
        /// <param name="fillContent">填充內容</param>
        /// <param name="isBold">是否加粗</param>
        /// <param name="fontSize">字型大小</param>
        /// <param name="fontFamily">字型</param>
        /// <param name="textAlign">文字顯示位置</param>
        /// <returns></returns>
        private static XWPFParagraph ParagraphInstanceSetting(XWPFDocument document, string fillContent, bool isBold, int fontSize, string fontFamily, ParagraphAlignment textAlign)
        {
            XWPFParagraph paragraph = document.CreateParagraph();//建立段落物件
            paragraph.Alignment = textAlign;//文字顯示位置  

            XWPFRun xwpfRun= paragraph.CreateRun();//建立段落文字物件
            xwpfRun.IsBold = isBold;//文字加粗
            xwpfRun.SetText(fillContent);//填充內容
            xwpfRun.FontSize = fontSize;//設定文字大小
            xwpfRun.SetFontFamily(fontFamily, FontCharRange.None); //設定標題樣式如:(微軟雅黑,隸書,楷體)根據自己的需求而定
            return paragraph;
        }

XWPFRun文字物件的屬性比較多,以下我簡單說明常用的幾種方式:

            XWPFParagraph paragraph = document.CreateParagraph();//建立段落物件

            XWPFRun xwpfRun= paragraph.CreateRun();//建立段落文字物件
            xwpfRun.IsBold = isBold;//文字加粗
            xwpfRun.SetText(fillContent);//填充內容
            xwpfRun.FontSize = fontSize;//設定文字大小
            xwpfRun.SetFontFamily(fontFamily, FontCharRange.None); //設定標題樣式如:(微軟雅黑,隸書,楷體)根據自己的需求而定
            xwpfRun.SetColor("BED4F1");//設定字型顏色--十六進位制
            xwpfRun.IsDoubleStrikeThrough=true;//是否顯示雙刪除線
            xwpfRun.IsStrikeThrough = true;//是否顯示單刪除線
            xwpfRun.SetUnderline(UnderlinePatterns.Dash);//設定下劃線,列舉型別
            xwpfRun.SetTextPosition(20);//設定文字位置(設定兩行之間的行間)
            xwpfRun.AddBreak();//設定換行(</br>)
            xwpfRun.AddTab();//新增tab鍵
            xwpfRun.AddCarriageReturn();//添加回車鍵
            xwpfRun.IsImprinted = true;//印跡(懸浮陰影),效果和浮雕類似
            xwpfRun.IsItalic=true;//是否設定斜體(字型傾斜)
            xwpfRun.Subscript = VerticalAlign.SUBSCRIPT;//設定下標,列舉型別

NPOI中關於XWPFRun屬性的更多使用技巧,請閱讀原始碼:

原始碼地址:https://github.com/tonyqus/npoi/blob/master/ooxml/XWPF/Usermodel/XWPFRun.cs

四、NPOI生成Word完整程式碼:

using Microsoft.AspNetCore.Hosting;
using NPOI.OpenXmlFormats.Wordprocessing;
using NPOI.XWPF.UserModel;
using System;
using System.IO;

namespace Export.Services
{
    public class NpoiWordExportService
    {
        private static IHostingEnvironment _environment;

        public NpoiWordExportService(IHostingEnvironment iEnvironment)
        {
            _environment = iEnvironment;
        }

        #region 生成word

        /// <summary>
        ///  生成word文件,並儲存靜態資原始檔夾(wwwroot)下的SaveWordFile資料夾中
        /// </summary>
        /// <param name="savePath">儲存路徑</param>
        public bool SaveWordFile(out string savePath)
        {
            savePath = "";
            try
            {
                string currentDate = DateTime.Now.ToString("yyyyMMdd");
                string checkTime = DateTime.Now.ToString("yyyy年MM月dd日");//檢查時間
                //儲存檔案到靜態資源wwwroot,使用絕對路徑路徑
                var uploadPath = _environment.WebRootPath + "/SaveWordFile/" + currentDate + "/";//>>>相當於HttpContext.Current.Server.MapPath("") 

                string workFileName = checkTime + "追逐時光企業員工培訓考核統計記錄表";
                string fileName = string.Format("{0}.docx", workFileName, System.Text.Encoding.UTF8);

                if (!Directory.Exists(uploadPath))
                {
                    Directory.CreateDirectory(uploadPath);
                }

                //通過使用檔案流,建立檔案流物件,向檔案流中寫入內容,並儲存為Word文件格式
                using (var stream = new FileStream(Path.Combine(uploadPath, fileName), FileMode.Create, FileAccess.Write))
                {
                    //建立document文件物件物件例項
                    XWPFDocument document = new XWPFDocument();

                    /**
                     *這裡我通過設定公共的Word文件中SetParagraph(段落)例項建立和段落樣式格式設定,大大減少了程式碼的冗餘,
                     * 避免每使用一個段落而去建立一次段落例項和設定段落的基本樣式
                     *(如下,ParagraphInstanceSetting為段落例項建立和樣式設定,後面索引表示為當前是第幾行段落,索引從0開始)
                     */
                    //文字標題
                    document.SetParagraph(ParagraphInstanceSetting(document, workFileName, true, 19, "宋體", ParagraphAlignment.CENTER), 0);

                    //TODO:這裡一行需要顯示兩個文字
                    document.SetParagraph(ParagraphInstanceSetting(document, $"編號:20190927101120445887", false, 14, "宋體", ParagraphAlignment.CENTER, true, $"    檢查時間:{checkTime}"), 1);


                    document.SetParagraph(ParagraphInstanceSetting(document, "登記機關:企業員工監督檢查機構", false, 14, "宋體", ParagraphAlignment.LEFT), 2);


                    #region 文件第一個表格物件例項
                    //建立文件中的表格物件例項
                    XWPFTable firstXwpfTable = document.CreateTable(4, 4);//顯示的行列數rows:3行,cols:4列
                    firstXwpfTable.Width = 5200;//總寬度
                    firstXwpfTable.SetColumnWidth(0, 1300); /* 設定列寬 */
                    firstXwpfTable.SetColumnWidth(1, 1100); /* 設定列寬 */
                    firstXwpfTable.SetColumnWidth(2, 1400); /* 設定列寬 */
                    firstXwpfTable.SetColumnWidth(3, 1400); /* 設定列寬 */

                    //Table 表格第一行展示...後面的都是一樣,只改變GetRow中的行數
                    firstXwpfTable.GetRow(0).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "企業名稱", ParagraphAlignment.CENTER, 40, true));
                    firstXwpfTable.GetRow(0).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "追逐時光", ParagraphAlignment.CENTER, 40, false));
                    firstXwpfTable.GetRow(0).GetCell(2).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "企業地址", ParagraphAlignment.CENTER, 40, true));
                    firstXwpfTable.GetRow(0).GetCell(3).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "湖南省-長沙市-嶽麓區", ParagraphAlignment.CENTER, 40, false));

                    //Table 表格第二行
                    firstXwpfTable.GetRow(1).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "聯絡人", ParagraphAlignment.CENTER, 40, true));
                    firstXwpfTable.GetRow(1).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "小明同學", ParagraphAlignment.CENTER, 40, false));
                    firstXwpfTable.GetRow(1).GetCell(2).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "聯絡方式", ParagraphAlignment.CENTER, 40, true));
                    firstXwpfTable.GetRow(1).GetCell(3).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "151****0456", ParagraphAlignment.CENTER, 40, false));


                    //Table 表格第三行
                    firstXwpfTable.GetRow(2).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "企業許可證號", ParagraphAlignment.CENTER, 40, true));
                    firstXwpfTable.GetRow(2).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "XXXXX-66666666", ParagraphAlignment.CENTER, 40, false));
                    firstXwpfTable.GetRow(2).GetCell(2).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "檢查次數", ParagraphAlignment.CENTER, 40, true));
                    firstXwpfTable.GetRow(2).GetCell(3).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, $"本年度檢查8次", ParagraphAlignment.CENTER, 40, false));


                    firstXwpfTable.GetRow(3).MergeCells(0, 3);//合併3列
                    firstXwpfTable.GetRow(3).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "", ParagraphAlignment.LEFT, 10, false));

                    #endregion

                    var checkPeopleNum = 0;//檢查人數
                    var totalScore = 0;//總得分

                    #region 文件第二個表格物件例項(遍歷表格項)
                    //建立文件中的表格物件例項
                    XWPFTable secoedXwpfTable = document.CreateTable(5, 4);//顯示的行列數rows:8行,cols:4列
                    secoedXwpfTable.Width = 5200;//總寬度
                    secoedXwpfTable.SetColumnWidth(0, 1300); /* 設定列寬 */
                    secoedXwpfTable.SetColumnWidth(1, 1100); /* 設定列寬 */
                    secoedXwpfTable.SetColumnWidth(2, 1400); /* 設定列寬 */
                    secoedXwpfTable.SetColumnWidth(3, 1400); /* 設定列寬 */

                    //遍歷表格標題
                    secoedXwpfTable.GetRow(0).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "員工姓名", ParagraphAlignment.CENTER, 40, true));
                    secoedXwpfTable.GetRow(0).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "性別", ParagraphAlignment.CENTER, 40, true));
                    secoedXwpfTable.GetRow(0).GetCell(2).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "年齡", ParagraphAlignment.CENTER, 40, true));
                    secoedXwpfTable.GetRow(0).GetCell(3).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "綜合評分", ParagraphAlignment.CENTER, 40, true));

                    //遍歷四條資料
                    for (var i = 1; i < 5; i++)
                    {
                        secoedXwpfTable.GetRow(i).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "小明" + i + "號", ParagraphAlignment.CENTER, 40, false));
                        secoedXwpfTable.GetRow(i).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, "男", ParagraphAlignment.CENTER, 40, false));
                        secoedXwpfTable.GetRow(i).GetCell(2).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, 20 + i + "歲", ParagraphAlignment.CENTER, 40, false));
                        secoedXwpfTable.GetRow(i).GetCell(3).SetParagraph(SetTableParagraphInstanceSetting(document, firstXwpfTable, 90 + i + "分", ParagraphAlignment.CENTER, 40, false));

                        checkPeopleNum++;
                        totalScore += 90 + i;
                    }

                    #endregion

                    #region 文件第三個表格物件例項
                    //建立文件中的表格物件例項
                    XWPFTable thirdXwpfTable = document.CreateTable(5, 4);//顯示的行列數rows:5行,cols:4列
                    thirdXwpfTable.Width = 5200;//總寬度
                    thirdXwpfTable.SetColumnWidth(0, 1300); /* 設定列寬 */
                    thirdXwpfTable.SetColumnWidth(1, 1100); /* 設定列寬 */
                    thirdXwpfTable.SetColumnWidth(2, 1400); /* 設定列寬 */
                    thirdXwpfTable.SetColumnWidth(3, 1400); /* 設定列寬 */
                    //Table 表格第一行,後面的合併3列(注意關於表格中行合併問題,先合併,後填充內容)
                    thirdXwpfTable.GetRow(0).MergeCells(0, 3);//從第一列起,合併3列
                    thirdXwpfTable.GetRow(0).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "檢查內容: " +
                        $"於{checkTime}下午檢查了追逐時光企業員工培訓考核並對員工的相關資訊進行了相關統計,統計結果如下:                                                                                                                                                                                                                " +
                        "-------------------------------------------------------------------------------------" +
                        $"共對該企業({checkPeopleNum})人進行了培訓考核,培訓考核總得分為({totalScore})分。 " + "", ParagraphAlignment.LEFT, 30, false));


                    //Table 表格第二行
                    thirdXwpfTable.GetRow(1).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "檢查結果: ", ParagraphAlignment.CENTER, 40, true));
                    thirdXwpfTable.GetRow(1).MergeCells(1, 3);//從第二列起,合併三列
                    thirdXwpfTable.GetRow(1).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "該企業非常優秀,堅持每天學習打卡,具有蓬勃向上的活力。", ParagraphAlignment.LEFT, 40, false));

                    //Table 表格第三行
                    thirdXwpfTable.GetRow(2).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "處理結果: ", ParagraphAlignment.CENTER, 40, true));
                    thirdXwpfTable.GetRow(2).MergeCells(1, 3);
                    thirdXwpfTable.GetRow(2).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "通過檢查,評分為優秀!", ParagraphAlignment.LEFT, 40, false));

                    //Table 表格第四行,後面的合併3列(注意關於表格中行合併問題,先合併,後填充內容),額外說明
                    thirdXwpfTable.GetRow(3).MergeCells(0, 3);//合併3列
                    thirdXwpfTable.GetRow(3).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "備註說明: 記住,堅持就是勝利,永遠保持一種求知,好問的心理!", ParagraphAlignment.LEFT, 30, false));

                    //Table 表格第五行
                    thirdXwpfTable.GetRow(4).MergeCells(0, 1);
                    thirdXwpfTable.GetRow(4).GetCell(0).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "                                                                                                                                                                                                 檢查人員簽名:              年 月 日", ParagraphAlignment.LEFT, 40, false));
                    thirdXwpfTable.GetRow(4).MergeCells(1, 2);

                    thirdXwpfTable.GetRow(4).GetCell(1).SetParagraph(SetTableParagraphInstanceSetting(document, thirdXwpfTable, "                                                                                                                                                                                                 企業法人簽名:              年 月 日", ParagraphAlignment.LEFT, 40, false));


                    #endregion

                    //向文件流中寫入內容,生成word
                    document.Write(stream);

                    savePath = "/SaveWordFile/" + currentDate + "/" + fileName;

                    return true;
                }
            }
            catch (Exception ex)
            {
                //ignore
                savePath = ex.Message;
                return false;
            }
        }


        /// <summary>
        /// 建立word文件中的段落物件和設定段落文字的基本樣式(字型大小,字型,字型顏色,字型對齊位置)
        /// </summary>
        /// <param name="document">document文件物件</param>
        /// <param name="fillContent">段落第一個文字物件填充的內容</param>
        /// <param name="isBold">是否加粗</param>
        /// <param name="fontSize">字型大小</param>
        /// <param name="fontFamily">字型</param>
        /// <param name="paragraphAlign">段落排列(左對齊,居中,右對齊)</param>
        /// <param name="isStatement">是否在同一段落建立第二個文字物件(解決同一段落裡面需要填充兩個或者多個文字值的情況,多個文字需要自己拓展,現在最多支援兩個)</param>
        /// <param name="secondFillContent">第二次宣告的文字物件填充的內容,樣式與第一次的一致</param>
        /// <returns></returns>
        private static XWPFParagraph ParagraphInstanceSetting(XWPFDocument document, string fillContent, bool isBold, int fontSize, string fontFamily, ParagraphAlignment paragraphAlign, bool isStatement = false, string secondFillContent = "")
        {
            XWPFParagraph paragraph = document.CreateParagraph();//建立段落物件
            paragraph.Alignment = paragraphAlign;//文字顯示位置,段落排列(左對齊,居中,右對齊)

            XWPFRun xwpfRun = paragraph.CreateRun();//建立段落文字物件
            xwpfRun.IsBold = isBold;//文字加粗
            xwpfRun.SetText(fillContent);//填充內容
            xwpfRun.FontSize = fontSize;//設定文字大小
            xwpfRun.SetFontFamily(fontFamily, FontCharRange.None); //設定標題樣式如:(微軟雅黑,隸書,楷體)根據自己的需求而定

            if (isStatement)
            {
                XWPFRun secondxwpfRun = paragraph.CreateRun();//建立段落文字物件
                secondxwpfRun.IsBold = isBold;//文字加粗
                secondxwpfRun.SetText(secondFillContent);//填充內容
                secondxwpfRun.FontSize = fontSize;//設定文字大小
                secondxwpfRun.SetFontFamily(fontFamily, FontCharRange.None); //設定標題樣式如:(微軟雅黑,隸書,楷體)根據自己的需求而定
            }


            return paragraph;
        }

        /// <summary>  
        /// 建立Word文件中表格段落例項和設定表格段落文字的基本樣式(字型大小,字型,字型顏色,字型對齊位置)
        /// </summary>  
        /// <param name="document">document文件物件</param>  
        /// <param name="table">表格物件</param>  
        /// <param name="fillContent">要填充的文字</param>  
        /// <param name="paragraphAlign">段落排列(左對齊,居中,右對齊)</param>
        /// <param name="rowsHeight">設定文字位置(設定兩行之間的行間),從而實現table的高度設定效果  </param>
        /// <param name="isBold">是否加粗(true加粗,false不加粗)</param>
        /// <param name="fontSize">字型大小</param>
        /// <returns></returns>  
        private static XWPFParagraph SetTableParagraphInstanceSetting(XWPFDocument document, XWPFTable table, string fillContent, ParagraphAlignment paragraphAlign, int rowsHeight, bool isBold, int fontSize = 10)
        {
            var para = new CT_P();
            XWPFParagraph paragraph = new XWPFParagraph(para, table.Body);//建立表格中的段落物件
            paragraph.Alignment = paragraphAlign;//文字顯示位置,段落排列(左對齊,居中,右對齊)

            XWPFRun xwpfRun = paragraph.CreateRun();//建立段落文字物件
            xwpfRun.SetText(fillContent);
            xwpfRun.FontSize = fontSize;//字型大小
            xwpfRun.IsBold = isBold;//是否加粗
            xwpfRun.SetFontFamily("宋體", FontCharRange.None);//設定字型(如:微軟雅黑,華文楷體,宋體)
            xwpfRun.SetTextPosition(rowsHeight);//設定文字位置(設定兩行之間的行間),從而實現table的高度設定效果 
            return paragraph;
        }

        #endregion


    }
}

總結:

  寫到最後我只想說我太難了,為了實現我想要的這個樣式,通過不斷的查閱資料,理解NPOI中的段落,文字基本屬性。最終在我生成第22個word時,總算是達到了我要的效果。這裡我將自己在網上查閱的一些資料和自己的實踐心得分享給大家,希望能夠幫到大家,別忘了給我star喲。

GitHub完整示例地址:https://github.com/YSGStudyHards/NPOI-Export-Word