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;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章