C#调用NPOI组件读取excel表格数据转为datatable写入word表格中并向word中插入图片/文字/书签 获得书签列表
调用word的com组件将400条数据导入word表格中耗时10分钟简直不能忍受,使用NPOI组件耗时4秒钟.但是NPOI中替换书签内容的功能不知道是不支持还是没找到.
辅助类 Excel表格数据与DataTable互转:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using NPOI.HSSF.UserModel; using System.IO; using System.Data; namespace Utils { //http://www.cnblogs.com/luxiaoxun/p/3374992.html public class ExcelDataTableConverter : IDisposable { private string fileName = null; //文件名 private IWorkbook workbook = null; private FileStream fs = null; private bool disposed; public ExcelDataTableConverter(string fileName) { this.fileName = fileName; disposed = false; } /// <summary> /// 将DataTable数据导入到excel中 /// </summary> /// <param name="data">要导入的数据</param> /// <param name="isColumnWritten">DataTable的列名是否要导入</param> /// <param name="sheetName">要导入的excel的sheet的名称</param> /// <returns>导入数据行数(包含列名那一行)</returns> public int DataTableToExcel(DataTable data, string sheetName, bool isColumnWritten) { int i = 0; int j = 0; int count = 0; ISheet sheet = null; fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite); if (fileName.IndexOf(".xlsx") > 0) // 2007版本 workbook = new XSSFWorkbook(); else if (fileName.IndexOf(".xls") > 0) // 2003版本 workbook = new HSSFWorkbook(); try { if (workbook != null) { sheet = workbook.CreateSheet(sheetName); } else { return -1; } if (isColumnWritten == true) //写入DataTable的列名 { IRow row = sheet.CreateRow(0); for (j = 0; j < data.Columns.Count; ++j) { row.CreateCell(j).SetCellValue(data.Columns[j].ColumnName); } count = 1; } else { count = 0; } for (i = 0; i < data.Rows.Count; ++i) { IRow row = sheet.CreateRow(count); for (j = 0; j < data.Columns.Count; ++j) { row.CreateCell(j).SetCellValue(data.Rows[i][j].ToString()); } ++count; } workbook.Write(fs); //写入到excel return count; } catch (Exception ex) { Console.WriteLine("Exception: " + ex.Message); return -1; } } /// <summary> /// 将excel中的数据导入到DataTable中 /// </summary> /// <param name="sheetName">excel工作薄sheet的名称</param> /// <param name="isFirstRowColumn">第一行是否是DataTable的列名</param> /// <returns>返回的DataTable</returns> public DataTable ExcelToDataTable(string sheetName, bool isFirstRowColumn) { ISheet sheet = null; DataTable data = new DataTable(); int startRow = 0; try { fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); if (fileName.IndexOf(".xlsx") > 0) // 2007版本 workbook = new XSSFWorkbook(fs); else if (fileName.IndexOf(".xls") > 0) // 2003版本 workbook = new HSSFWorkbook(fs); if (sheetName != null) { sheet = workbook.GetSheet(sheetName); if (sheet == null) //如果没有找到指定的sheetName对应的sheet,则尝试获取第一个sheet { sheet = workbook.GetSheetAt(0); } } else { sheet = workbook.GetSheetAt(0); } if (sheet != null) { IRow firstRow = sheet.GetRow(0); int cellCount = firstRow.LastCellNum; //一行最后一个cell的编号 即总的列数 if (isFirstRowColumn) { for (int i = firstRow.FirstCellNum; i < cellCount; ++i) { ICell cell = firstRow.GetCell(i); if (cell != null) { string cellValue = cell.StringCellValue; if (cellValue != null) { DataColumn column = new DataColumn(cellValue); data.Columns.Add(column); } } } startRow = sheet.FirstRowNum + 1; } else { startRow = sheet.FirstRowNum; } //最后一列的标号 int rowCount = sheet.LastRowNum; for (int i = startRow; i <= rowCount; ++i) { IRow row = sheet.GetRow(i); if (row == null) continue; //没有数据的行默认是null DataRow dataRow = data.NewRow(); for (int j = row.FirstCellNum; j < cellCount; ++j) { if (row.GetCell(j) != null) //同理,没有数据的单元格都默认是null { //dataRow[j] = row.GetCell(j).ToString(); //19890603会显示成MIDB(C2,7,6) dataRow[j] = row.GetCell(j).StringCellValue; } } data.Rows.Add(dataRow); } } return data; } catch (Exception ex) { Console.WriteLine("Exception: " + ex.Message); return null; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!this.disposed) { if (disposing) { if (fs != null) fs.Close(); } fs = null; disposed = true; } } } }
调用:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using NPOI.XWPF.UserModel; using NPOI.OpenXmlFormats.Wordprocessing; using System.IO; namespace WordNPOI { public partial class Form1 : Form { public Form1() { InitializeComponent(); } XWPFDocument doc = new XWPFDocument(); private void Form1_Load(object sender, EventArgs e) { } private void button1_Click(object sender, EventArgs e) { Utils.ExcelDataTableConverter edc = new Utils.ExcelDataTableConverter("60岁以上人员.xlsx"); DataTable dt = edc.ExcelToDataTable("sheet1", true); int rowCount = dt.Rows.Count; int columnCount = dt.Columns.Count; //创建段落对象 XWPFParagraph p1 = doc.CreateParagraph(); //创建run对象 //本节提到的所有样式都是基于XWPFRun的, //你可以把XWPFRun理解成一小段文字的描述对象, //这也是Word文档的特征,即文本描述性文档。 //来自Tony Qu http://tonyqus.sinaapp.com/archives/609 XWPFRun r1 = p1.CreateRun(); r1.SetBold(true); r1.SetText("数据导出demo"); r1.SetBold(true); r1.FontFamily = "宋体"; r1.FontSize = 26; //r1.SetFontFamily("Arial");//设置雅黑字体 //创建表格对象,行 列 XWPFTable table = doc.CreateTable(rowCount + 1, columnCount); //添加列头 for (int i = 0; i < columnCount; i++) { var ctt = table.GetRow(0).GetCell(i).GetCTTc(); var tcPr = ctt.AddNewTcPr(); tcPr.tcW = new CT_TblWidth(); tcPr.tcW.w = "6000";//单元格宽 单位是EMU,1英寸= 914400 EMU http://bbs.csdn.net/topics/390906055 tcPr.tcW.type = ST_TblWidth.dxa; //table.SetColumnWidth(i, 5500); //无效果 特定数字5500又太大 貌似要设置为n*256 //设置单元格字体 样式 http://www.tuicool.com/articles/JFZzUj var p = table.GetRow(0).GetCell(i).AddParagraph(); XWPFRun r = p.CreateRun(); r.FontFamily = "宋体"; r.FontSize = 10; r.SetBold(true); r.SetText(dt.Columns[i].ColumnName); } for (int i = 1; i < rowCount; i++) { for (int j = 0; j < columnCount; j++) { string tem = dt.Rows[i][j].ToString(); table.GetRow(i).GetCell(j).SetText(dt.Rows[i][j].ToString()); } } //插入图片 var gfs = new FileStream("1.png", FileMode.Open, FileAccess.Read); var gp = doc.CreateParagraph(); gp.Alignment = ParagraphAlignment.CENTER; //居中 var gr = gp.CreateRun(); gr.AddPicture(gfs, (int)PictureType.JPEG, "1.png", 1000000, 1000000); //1000000 差不多100像素多一点 gfs.Close(); var gp1 = doc.CreateParagraph(); gp1.CreateRun().SetText("The End"); //bookmark书签 var items = doc.Paragraphs[0].Document.BodyElements; //CT_Bookmark b = new CT_Bookmark(); //CT_Bookmark ctBookmark = doc.Paragraphs[0].GetBookmarkStartArray(0); //foreach (CT_Bookmark bookmark in doc.Paragraphs[0].GetCTP().GetBookmarkStartList()) //网上的代码,发现找不到这个方法 //{ // Assert.AreEqual("poi", bookmark.name); //} //书签0开始 int m_bookId = 0;//同一段内有多个书签,需要不同的Id,不同段的书签Id可以相同 CT_P m_p = doc.Document.body.AddNewP(); m_p.AddNewPPr().AddNewJc().val = ST_Jc.both; m_p.AddNewPPr().AddNewSpacing().line = "400";//固定行距20磅 m_p.AddNewPPr().AddNewSpacing().lineRule = ST_LineSpacingRule.exact; m_p.Items = new System.Collections.ArrayList(); CT_Bookmark m_ctbook1 = new CT_Bookmark(); m_bookId = m_p.Items.Count; m_ctbook1.id = m_bookId.ToString(); //"0"; m_ctbook1.name = "NPOI1";//书签名,超链接用 m_p.Items.Add(m_ctbook1); m_p.ItemsElementName = new List<ParagraphItemsChoiceType>(); m_p.ItemsElementName.Add(ParagraphItemsChoiceType.bookmarkStart); m_p.AddNewR().AddNewT().Value = "1、NPOI介绍"; //书签0结束 m_ctbook1 = new CT_Bookmark(); m_ctbook1.id = m_bookId.ToString();//"0"; m_p.Items.Add(m_ctbook1); m_p.ItemsElementName.Add(ParagraphItemsChoiceType.bookmarkEnd); //获得书签 int pcount = doc.Document.body.ItemsElementName.Count(); List<CT_Bookmark> bkList = new List<CT_Bookmark>(); for (int i = 0; i < pcount; i++) { var ctp = doc.Document.body.GetPArray(i); if (ctp!=null) { var tempBookMarkList = ctp.GetBookmarkStartList(); bkList.AddRange(tempBookMarkList); } } foreach (var bookMark in bkList) {//替换书签内容?????????????????找不到示例代码还是不支持此功能? bookMark.colFirst = "1"; bookMark.colLast = "2"; bookMark.displacedByCustomXmlSpecified = true; MessageBox.Show(bookMark.name); } //保存文件到磁盘 FileStream out1 = new FileStream("simpleTable.docx", FileMode.OpenOrCreate); doc.Write(out1); out1.Close(); MessageBox.Show("done"); } } }
关于宽高设置:
http://www.oschina.net/code/snippet_222150_9780
在Excel中,单元格的宽度其实就是列的宽度,因为Excel假设这一列的单元格的宽度肯定一致。所以要设置单元格的宽度,我们就得从列的宽度下手,HSSFSheet有个方法叫SetColumnWidth,共有两个参数:一个是列的索引(从0开始),一个是宽度。 现在假设你要设置B列的宽度,就可以用下面的代码: HSSFWorkbook hssfworkbook = new HSSFWorkbook(); HSSFSheet sheet1 = hssfworkbook.CreateSheet("Sheet1"); sheet1.SetColumnWidth(1, 100 * 256); 这里你会发现一个有趣的现象,SetColumnWidth的第二个参数要乘以256,这是怎么回事呢?其实,这个参数的单位是1/256个字符宽度,也就是说,这里是把B列的宽度设置为了100个字符。 刚才说的是如何设置,那如何去读取一个列的宽度呢?直接用GetColumnWidth方法,这个方法只有一个参数,那就是列的索引号。如下所示: int col1width = sheet1.GetColumnWidth(1); 说完宽度,我们来说高度,在Excel中,每一行的高度也是要求一致的,所以设置单元格的高度,其实就是设置行的高度,所以相关的属性也应该在HSSFRow上,它就是HSSFRow.Height和HeightInPoints,这两个属性的区别在于HeightInPoints的单位是点,而Height的单位是1/20个点,所以Height的值永远是HeightInPoints的20倍。 要设置第一行的高度,可以用如下代码: sheet1.CreateRow(0).Height = 200*20; 或者 sheet1.CreateRow(0).HeightInPoints = 200; 如果要获得某一行的行高,可以直接拿HSSFRow.Height属性的返回值。 你可能觉得一行一行设置行高或者一列一列设置列宽很麻烦,那你可以考虑使用HSSFSheet.DefaultColumnWidth、HSSFSheet.DefaultRowHeight和HSSFSheet.DefaultRowHeightInPoints属性。 一旦设置了这些属性,如果某一行或者某一列没有设置宽度,就会使用默认宽度或高度。代码如下: sheet1.DefaultColumnWidth=100*256; sheet1.DefaultRowHeight=30*20;
版权声明:本文为博主原创文章,未经博主允许不得转载。
作者:xuejianxiyang
出处:http://xuejianxiyang.cnblogs.com
关于作者:Heaven helps those who help themselves.
本文版权归原作者和博客园共有,欢迎转载,但未经原作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。