C# 生成word文档(NPOI.XWPF)

时间:2024-02-17 14:02:08

一、基础

1、创建Word

using NPOI.XWPF.UserModel
        XWPFDocument doc = new XWPFDocument();      //创建新的word文档
        
        XWPFParagraph p1 = doc.CreateParagraph();   //向新文档中添加段落
        p1.SetAlignment(ParagraphAlignment.CENTER); //段落对其方式为居中

        XWPFRun r1 = p1.CreateRun();                //向该段落中添加文字
        r1.SetText("测试段落一");

        XWPFParagraph p2 = doc.CreateParagraph();
        p2.SetAlignment(ParagraphAlignment.LEFT);

        XWPFRun r2 = p2.CreateRun();
        r2.SetText("测试段落二");
     r2.SetFontSize(16);//设置字体大小
       r2.SetBlod(true);//设置粗体

        FileStream sw = File.Create("cutput.docx"); //...
        doc.Write(sw);                              //...
        sw.Close();                                 //在服务端生成文件

        FileInfo file = new FileInfo("cutput.docx");//文件保存路径及名称  
                                                    //注意: 文件保存的父文件夹需添加Everyone用户,并给予其完全控制权限
        Response.Clear();
        Response.ClearHeaders();
        Response.Buffer = false;
        Response.ContentType = "application/octet-stream";
        Response.AppendHeader("Content-Disposition", "attachment;filename=" 
            + HttpUtility.UrlEncode("output.docx", System.Text.Encoding.UTF8));
        Response.AppendHeader("Content-Length", file.Length.ToString());
        Response.WriteFile(file.FullName);
        Response.Flush();                           //以上将生成的word文件发送至用户浏览器

        File.Delete("cutput.docx");

2、特殊字符

代码实现起来很简单。

run之前的代码就不写了。大家可以网上搜索。

run.FontFamily = "Wingdings 2";//这边是特殊字符的字体
text = text.Replace("name", Convert.ToChar(0x0052).ToString());//0x0052是特殊字符的十六进制代码
//text = text.Replace("name", "R");//该代码也可以实现(0x0052对应的字符就是R)

3、NOPI读取Word模板并渲染保存

using NPOI.XWPF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;


namespace TestNPOI
{
    public class NPOIHleper
    {

        public static void Export()
        {
            string filepath = HttpContext.Current.Server.MapPath("~/simpleTable.docx");
            var tt = new  { name = "cjc", age = 29 };
            using (FileStream stream = File.OpenRead(filepath))
            {
                XWPFDocument doc = new XWPFDocument(stream);
                //遍历段落                  
                foreach (var para in doc.Paragraphs)
                {
                    ReplaceKey(para, tt);
                }                    //遍历表格      
                var tables = doc.Tables;
                foreach (var table in tables)
                {
                    foreach (var row in table.Rows)
                    {
                        foreach (var cell in row.GetTableCells())
                        {
                            foreach (var para in cell.Paragraphs)
                            {
                                ReplaceKey(para, tt);
                            }
                        }
                    }
                }

                FileStream out1 = new FileStream(HttpContext.Current.Server.MapPath("~/simpleTable" + DateTime.Now.Ticks + ".docx"), FileMode.Create);
                doc.Write(out1);
                out1.Close();
            }
        }

        private static void ReplaceKey(XWPFParagraph para, object model)
        {
            string text = para.ParagraphText;
            var runs = para.Runs;
            string styleid = para.Style;
            for (int i = 0; i < runs.Count; i++)
            {
                var run = runs[i];
                text = run.ToString();
                Type t = model.GetType();
                PropertyInfo[] pi = t.GetProperties();
                foreach (PropertyInfo p in pi)
                {
                    //$$与模板中$$对应,也可以改成其它符号,比如{$name},务必做到唯一
                    if (text.Contains("$" + p.Name + "$"))
                    {
                        text = text.Replace("$" + p.Name + "$", p.GetValue(model, null).ToString());
                    }
                }
                runs[i].SetText(text, 0);
            }
        }

    }
}

 

模板:

111

结果:

222

 

二、实践(渲染Word模板、插入特殊字符、指定表格位置插入行)

1、项目搭建

1、创建项目

2、创建类库和引入NPOI

 

 报错

  报搜尝试解决方案一

在项目下面建立upload文件夹,然后使用相对路径访问。

 在其他目录下请把upload目录权限授予asp.net用户。

 

 最后直接暴力EveryOney  也无效,找到的原因是参数位置搞错了,文件名+路径最后改为 路径+文件名的方式
 

3、贴上代码

using NPOI.XWPF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;

namespace NPOITest
{
    public class NPOIHleper
    {


        /// <summary>
        /// 输出模板docx文档
        /// </summary>
        /// <param name="tempFilePath">模板文件地址</param>
        /// <param name="outFolder">输出文件夹</param >
        /// <param name="fileName">文件名</param>
        /// <param name="data">数据格式Json->new { name = "cjc", age = 29 }</param>
        public static void CreateWord(string tempFilePath, string outFolder, string fileName, object data)
        {
            using (FileStream stream = File.OpenRead(tempFilePath))
            {
                XWPFDocument doc = new XWPFDocument(stream);
                //遍历段落                  
                foreach (var para in doc.Paragraphs)
                {
                    ReplaceKey(para, data);
                }   //遍历表格      
                var tables = doc.Tables;
                foreach (var table in tables)
                {
                    foreach (var row in table.Rows)
                    {
                        foreach (var cell in row.GetTableCells())
                        {
                            foreach (var para in cell.Paragraphs)
                            {
                                ReplaceKey(para, data);
                            }
                        }
                    }
                }
                var fullPath = Path.Combine(outFolder, fileName);
                FileStream outFile = new FileStream(fullPath, FileMode.Create);
                doc.Write(outFile);
                outFile.Close();
            }
        }
        /// <summary>
        /// 遍历替换段落位置字符
        /// </summary>
        /// <param name="para">段落参数</param>
        /// <param name="model">数据</param>
        private static void ReplaceKey(XWPFParagraph para, object model)
        {
            string text = para.ParagraphText;
            var runs = para.Runs;
            string styleid = para.Style;
            for (int i = 0; i < runs.Count; i++)
            {
                var run = runs[i];
                text = run.ToString();
                Type t = model.GetType();
                PropertyInfo[] pi = t.GetProperties();
                foreach (PropertyInfo p in pi)
                {
                    //$$与模板中$$对应,也可以改成其它符号,比如{$name},务必做到唯一
                    if (text.Contains("{$"+p.Name+"}"))
                    {
                        text = text.Replace("{$" + p.Name+"}", p.GetValue(model, null).ToString());
                    }
                }
                runs[i].SetText(text, 0);
            }
        }
    }
}

调用方式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace NPOITest.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var  data = new  { name = "cjc", age = 29 };
            string fileName = Guid.NewGuid() + "_声明.docx";
            string folder = Server.MapPath("~/upload"); //当前运行环境
            string tempTemplateFile = folder+"/测试.docx";
            string folders = "D:\\TempFile"; //当前运行环境
            NPOIHleper.CreateWord(tempTemplateFile, folders, fileName, data);
            //
            ViewBag.Title = "Home Page";

            return View();
        }
    }
}

对应模板

 

三、实践(指定表格位置插入行)

代码:

using NPOI.OpenXmlFormats.Wordprocessing;
using NPOI.XWPF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;

namespace NPOITest
{
    public class NPOIHleper
    {


        /// <summary>
        /// 输出模板docx文档
        /// </summary>
        /// <param name="tempFilePath">模板文件地址</param>
        /// <param name="outFolder">输出文件夹</param >
        /// <param name="fileName">文件名</param>
        /// <param name="data">数据格式Json->new { name = "cjc", age = 29 }</param>
        public static void CreateWord(string tempFilePath, string outFolder, string fileName, object data)
        {
            using (FileStream stream = File.OpenRead(tempFilePath))
            {
                XWPFDocument doc = new XWPFDocument(stream);
                //遍历段落                  
                foreach (var para in doc.Paragraphs)
                {
                    ReplaceKey(para, data);
                }   //遍历表格      
                var tables = doc.Tables;
                foreach (var table in tables)
                {
                    foreach (var row in table.Rows)
                    {
                        foreach (var cell in row.GetTableCells())
                        {
                            foreach (var para in cell.Paragraphs)
                            {
                                ReplaceKey(para, data);
                            }
                        }
                    }
                }
                //单独对表格新增
                var oprTable = tables[1];


                XWPFTableRow m_Row=oprTable.InsertNewTableRow(1);//创建一行/并且在某个位置添加一行
                m_Row.AddNewTableCell().SetText ("创建一行仅有一个单元格");


                //XWPFTableRow m_Row2 = oprTable.InsertNewTableRow(2);//创建一行/并且在某个位置添加一行
                ////m_Row2.AddNewTableCell().SetText("添加的新行");
                //XWPFTableCell cellCt_P = m_Row2.CreateCell();//创建一个单元格,创建单元格时就创建了一个CT_P

                //cellCt_P = m_Row2.CreateCell();
                //cellCt_P = m_Row2.CreateCell();

                ////单元格行和表
                //CT_Tc cttc = cellCt_P.GetCTTc();
                //CT_TcPr ctPr = cttc.AddNewTcPr();
                ////ctPr.gridSpan.val = "3";//合并3列
                //ctPr.AddNewVMerge().val = ST_Merge.restart;//合并行
                //cellCt_P.SetText("创建一行仅有一个单元格(合并后)");


                XWPFTableRow m_Row2 = oprTable.InsertNewTableRow(2);//创建一行/并且在某个位置添加一行
                XWPFTableCell tc3 = m_Row2.CreateCell();//创建单元格
                tc3.SetText("创建一行仅有一个单元格(合并后)");
                CT_Tc ct3 = tc3.GetCTTc();
                CT_TcPr cp3 = ct3.AddNewTcPr();
                cp3.gridSpan = new CT_DecimalNumber();
                cp3.gridSpan.val = "3"; //合并3列   


                XWPFTableRow m_Row3 = oprTable.InsertNewTableRow(2);//多个单元格以及合并
                m_Row3.AddNewTableCell().SetText("添加的新行单元格1");
                m_Row3.AddNewTableCell().SetText("添加的新行单元格2");
                m_Row3.AddNewTableCell().SetText("添加的新行单元格3");


                var fullPath = Path.Combine(outFolder, fileName);
                FileStream outFile = new FileStream(fullPath, FileMode.Create);
                doc.Write(outFile);
                outFile.Close();
            }
        }
        /// <summary>
        /// 遍历替换段落位置字符
        /// </summary>
        /// <param name="para">段落参数</param>
        /// <param name="model">数据</param>
        private static void ReplaceKey(XWPFParagraph para, object model)
        {
            string text = para.ParagraphText;
            var runs = para.Runs;
            string styleid = para.Style;
            for (int i = 0; i < runs.Count; i++)
            {
                var run = runs[i];
                text = run.ToString();
                Type t = model.GetType();
                PropertyInfo[] pi = t.GetProperties();
                foreach (PropertyInfo p in pi)
                {
                    //$$与模板中$$对应,也可以改成其它符号,比如{$name},务必做到唯一
                    if (text.Contains("{$" + p.Name + "}"))
                    {
                        text = text.Replace("{$" + p.Name + "}", p.GetValue(model, null).ToString());
                    }
                }
                runs[i].SetText(text, 0);
            }
        }
    }
}

结果:

 

 

 

四、实践(指定表格内单元格(字体)下划线+字符)

简单说明:

                XWPFParagraph p1 = doc.CreateParagraph(); //段落
                XWPFRun _run = p1.CreateRun();
                _run.SetText("一个单元格");
                _run.SetUnderline(UnderlinePatterns.Single);//段落下划线

既有文字加文字(下划线)

                XWPFTableRow m_Row2 = oprTable.InsertNewTableRow(2);//创建一行/并且在某个位置添加一行
                XWPFTableCell tc3 = m_Row2.CreateCell();//创建单元格
                //tc3.SetText("创建一行仅有一个单元格(合并后)");

                XWPFParagraph p1 = doc.CreateParagraph(); //段落
                XWPFRun _run = p1.CreateRun();
                _run.SetText("一个单元格");
                _run.SetUnderline(UnderlinePatterns.Single);//段落

                XWPFParagraph p12 = doc.CreateParagraph(); //无段落
                XWPFRun _run2 = p1.CreateRun();
                _run2.SetText("一个单元格");


                tc3.SetParagraph(p1);

 

 

这种写法我发现别扭,应该为

                //单独对表格新增
                var oprTable = tables[1];

                XWPFTableRow m_Row2 = oprTable.InsertNewTableRow(2);//创建一行/并且在某个位置添加一行
                XWPFTableCell tc3 = m_Row2.CreateCell();//创建单元格

                XWPFParagraph p1 = doc.CreateParagraph(); //段落1开始  1、注意这个段落是Doc创建的会导致表格外有段落出现
                XWPFRun _run = p1.CreateRun();
                _run.SetText("下划线");
                _run.SetUnderline(UnderlinePatterns.Single);//段落1结束

                //_run.AddCarriageReturn();2、注意只对表格外换行有效
                XWPFRun _run2 = p1.CreateRun();
                _run2.SetText("#####");

                tc3.SetParagraph(p1);

  发现我需要换行,思路还是不对,经过我读取拿到文档的数据结构,即表格的XWPFTableCell单元格paragraph属性如下:

 

经更改

 

                //单独对表格新增
                var oprTable = tables[1];

                XWPFTableRow m_Row2 = oprTable.InsertNewTableRow(2);//创建一行/并且在某个位置添加一行
                XWPFTableCell tc3 = m_Row2.CreateCell();//创建单元格

                XWPFParagraph p1 = tc3.AddParagraph();
                XWPFRun _run = p1.CreateRun();
                _run.SetText("下划线");
                _run.SetUnderline(UnderlinePatterns.Single);//段落1结束

                //_run.AddCarriageReturn();2、注意只对表格外换行有效
                XWPFParagraph p2 = tc3.AddParagraph();
                XWPFRun _run2 = p2.CreateRun();
                _run2.SetText("####");

                //这里设置了一下 //在复制这个就无效了 tc3.SetParagraph(p1); 想要添加通过(add方式)tc3.AddParagraph();
                tc3.SetParagraph(p1);

 

五、实践出现模板渲染替换问题

解决办法:删掉->重新写->可以从记事本复制

 

 

 其他妙用

//新建段落     

XWPFParagraph p1 = doc.CreateParagraph();

//对齐方式

 p1.SetAlignment(ParagraphAlignment.LEFT);

p1.SetVerticalAlignment(TextAlignment.AUTO);

//Word边框样式

p1.SetBorderBottom(Borders.DOUBLE);
p1.SetBorderTop(Borders.DOUBLE);
p1.SetBorderRight(Borders.DOUBLE);
p1.SetBorderLeft(Borders.DOUBLE);

p1.SetBorderBetween(Borders.SINGLE);

            //新建文字

            XWPFRun rUserHead = p1.CreateRun();

//文字内容

            rUserHead.SetText("员工 : ");

//颜色

            rUserHead.SetColor("4F6B72");

//大小

            rUserHead.SetFontSize(15);

//是否加粗

            rUserHead.SetBold(true);

//字体

            rUserHead.SetFontFamily("宋体");

//是否有下划线

            //r1.SetUnderline(UnderlinePatterns.DotDotDash);

//位置

            rUserHead.SetTextPosition(20);

//增加换行

rUserHead.AddCarriageReturn();

 

需求整理(动态在某个单元格内插入多个字段)

1、原本样子以及要实现的效果

 实现的效果

原因:

 

需求整理(动态插入表格)

1、原本样子以及要实现的效果