基於.NET的程序讀取Excel文件的解決方案


shanzm-2020年12月8日 23:48:11

0. 前言

以前基於 .NET 開發的程序,我一般都是使用NPOI操作Excel文件,

因為我的程序讀取的是另外一個成品WMS導出的Excel文件(至於為什么不直接從數據庫獲取該WMS導出到Excel的數據,此中原因,一言難盡!),在使用NPOI讀取Excel的時候,提示錯誤:

Initialisation of record 0x203(NumberRecord) left 4 bytes remaining still to be read.

StackOverFlow一個高贊答案就是升級NPOI版本,或使用Office Excel將Excel文件另存為,保存為新的Excel文件再使用NPOI讀取該Excel文件。

雖已使用最新的NPOI,但是依舊出現該錯誤,讓用戶另存為不合適,但是我又尚未找到一個簡單有效快速的處理方法。

考慮到服務器上已經安裝的Office套件,所以使用Com組件的方法操作Excel文件,但是讀取速度較慢。

最終嘗試使用ADO .NET 中的OleDbConnection類讀取Excel文件,讀取速度,且相對方便,簡單記錄如下。



1. 使用NPOI庫讀取Excel文件

在.NET程序中需要操作Excel文件,不論xls格式還是xlsx格式都是可以使用NPOI

Nuget獲取該擴展:

PM> Install-Package NPOI -Version 2.5.2

簡單的一個示例,讀取用戶上傳的Excel文件(只是一個簡單的可運行的示例,代碼完備性不高):

    /// <summary>
    /// 讀取Excel導入DataTable
    /// </summary>
    /// <param name="filepath">導入的文件路徑(包括文件名)</   param>
    /// <param name="sheetName">工作表名稱</param>
    /// <param name="isFirstRowColumn">第一行是否是DataTable的列    名</param>
    /// <returns>DataTable</returns>
    public static DataTable ExcelToDataTable(string filePath,string sheetName,bool isFirstRowColumn)
    {
        DataTable data = new DataTable();
        FileStream fs;
        int startRow = 0;
        using (fs = new FileStream(filePath, FileMode.Open,     FileAccess.Read))
        {
            try
            {
                IWorkbook workbook = filePath.Contains(".xlsx") ? (IWorkbook)new XSSFWorkbook(fs) :newHSSFWorkbook(fs);//xlsx使用XSSFWorkbook, xls使用HSSFWorkbokk
                ISheet sheet = workbook.GetSheet(sheetName) ?? workbook.GetSheetAt(0);//如果沒有找到指sheetName 對應的sheet,則嘗試獲取第一個sheet
                if (sheet != null)
                {
                    IRow firstrow = sheet.GetRow(0);//第一行
                    int firstCellNum = firstrow.FirstCellNum;// 行第一個cell的編號,從0開始
                    int lastCellNum = firstrow.LastCellNum; //  行最后一個cell的編號 即總的列數,(不忽略中間某    列空格)
                    if (isFirstRowColumn)//如果第一行是表格列頭
                    {
                        for (int i = firstCellNum; i < lastCellNum; 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 rowCont = sheet.LastRowNum;
                    for (int i = startRow; i <=rowCont; i++)
                    {
                        IRow row = sheet.GetRow(i);
                        DataRow dataRow = data.NewRow();
                        //判斷需要讀取的最后一行
                        if (row != null && (row.GetCell(row.FirstCellNum) != null && row.GetCel (rowFirstCellNum).ToString() != "合計")  )
                        {
                            for (int j = row.FirstCellNum; j < lastCellNum; j++)
                            {
                                dataRow[j] = row.GetCell(j).ToString();
                            }
                            data.Rows.Add(dataRow);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
                return data;
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception: " + ex.Message);
                return null;
            }
            finally 
            { 
                fs.Close(); 
                fs.Dispose(); 
            }
        }
    }

讀取到Excel表格中的數據到DataTable 中,可以使用SqlBulkCopy批量插入的方式將DataTable數據存儲到數據庫表中,關於ADO .NET 針對SQL Server的批量插入的各種方法以及比較,可以參考文末給出的參考連接

簡單示例如下:

        /// <summary> 
        /// 使用SqlBulkCopy將DataTable中的數據批量插入數據庫中
        /// 注意:DataTable中的列需要與數據庫表中的列完全一致。
        /// </summary> 
        /// <param name="conStr">數據庫連接串</param>
        /// <param name="dbTableName">數據庫中對應的表名</param> 
        /// <param name="dtData">數據集</param> 
        public static void SqlBulkCopyInsert(string dbTableName, DataTable dataTable)
        {
                using (SqlBulkCopy sqlRevdBulkCopy = new SqlBulkCopy(connStr))//引用SqlBulkCopy 
                {
                    sqlRevdBulkCopy.DestinationTableName = dbTableName;//數據庫中對應的表名 
                    sqlRevdBulkCopy.NotifyAfter = dataTable.Rows.Count;//有幾行數據 
                    sqlRevdBulkCopy.WriteToServer(dataTable);//數據導入數據庫 
                    sqlRevdBulkCopy.Close();//關閉連接 
                }
        }


2. 使用OleDbConnection

使用ADO .NET 中的OleDbConnection類連接查詢。簡單示例如下:

        /// <summary>
        /// 讀取Excel返回DataTable
        /// </summary>
        /// <param name="filePath">Excel文件路徑</param>
        /// <param name="tableName">Excel文件中Sheet名稱</param>
        /// <returns></returns>
        public static DataSet ExcelToDataSet(string filePath, string tableName)
        {
            //獲取文件擴展名
            string strExtension = System.IO.Path.GetExtension(filePath);
            OleDbConnection myConn = null;
            switch (strExtension)
            {
                case ".xls":
                    myConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + filePath + ";" + "Extended Properties=\"Excel 8.0;HDR=yes;IMEX=1;\"");
                    break;
                case ".xlsx":
                    myConn = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";" + "Extended Properties=\"Excel 12.0;HDR=yes;IMEX=1;\"");
                    //此連接可以操作.xls與.xlsx文件 (支持Excel2003 和 Excel2007 的連接字符串) 
                    //"HDR=yes;"是說Excel文件的第一行是列名而不是數,"HDR=No;"正好與前面的相反。"IMEX=1 "如果列中的數據類型不一致,使用"IMEX=1"可必免數據類型沖突。 
                    break;
                default:
                    myConn = null;
                    break;
            }
            if (myConn == null)
            {
                return null;
            }
            string strCom = " SELECT * FROM [" + tableName + "$]";
            myConn.Open();
            //獲取Excel指定Sheet表中的信息
            OleDbDataAdapter myCommand = new OleDbDataAdapter(strCom, myConn);
            DataSet ds;
            ds = new DataSet();
            myCommand.Fill(ds, tableName);
            myConn.Close();
            return ds.Tables[0];
        }

使用OleDbConnection在本地測試沒有問題,在發布部署在服務器后出現了一個異常:

未在本地計算機上注冊“Microsoft.Jet.OLEDB.4.0”提供程序

解決方法:IIS-->應用程序池-->選中部署的項目右鍵-->高級設置-->啟用32位應用程序-->True



3. 相關參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM