一、基础
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); } } } }
模板:
结果:
二、实践(渲染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、原本样子以及要实现的效果