轉載自:http://developer.51cto.com/art/201302/380622.htm,
方法一:OleDb: 用這種方法讀取Excel速度還是非常的快的,但這種方式讀取數據的時候不太靈活,不過可以在 DataTable 中對數據進行一些刪減修改。
優點:讀取方式簡單、讀取速度快
缺點:除了讀取過程不太靈活之外,這種讀取方式還有個弊端就是,當Excel數據量很大時。會非常占用內存,當內存不夠時會拋出內存溢出的異常。
不過一般情況下還是非常不錯的。
(代碼比原文相較有所修改)

DataTable GetDataFromExcelByConn(bool hasTitle = false) { OpenFileDialog openFile = new OpenFileDialog(); openFile.Filter = "Excel(*.xlsx)|*.xlsx|Excel(*.xls)|*.xls"; openFile.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); openFile.Multiselect = false; if (openFile.ShowDialog() == DialogResult.Cancel) return null; var filePath = openFile.FileName; string fileType = System.IO.Path.GetExtension(filePath); if (string.IsNullOrEmpty(fileType)) return null; using (DataSet ds = new DataSet()) { string strCon = string.Format("Provider=Microsoft.Jet.OLEDB.{0}.0;" + "Extended Properties=\"Excel {1}.0;HDR={2};IMEX=1;\";" + "data source={3};", (fileType == ".xls" ? 4 : 12), (fileType == ".xls" ? 8 : 12), (hasTitle ? "Yes" : "NO"), filePath); string strCom = " SELECT * FROM [Sheet1$]"; using (OleDbConnection myConn = new OleDbConnection(strCon)) using (OleDbDataAdapter myCommand = new OleDbDataAdapter(strCom, myConn)) { myConn.Open(); myCommand.Fill(ds); } if (ds == null || ds.Tables.Count <= 0) return null; return ds.Tables[0]; } }
方法二:Com組件的方式讀取Excel
這種方式需要先引用 Microsoft.Office.Interop.Excel 。首選說下這種方式的優缺點
優點:可以非常靈活的讀取Excel中的數據
缺點:如果是Web站點部署在IIS上時,還需要服務器機子已安裝了Excel,有時候還需要為配置IIS權限。最重要的一點因為是基於單元格方式讀取的,所以數據很慢(曾做過試驗,直接讀取千行、200多列的文件,直接讀取耗時15分鍾。即使采用多線程分段讀取來提高CPU的利用率也需要8分鍾。PS:CPU I3)
需要讀取大文件的的童鞋們慎重。。。
(代碼比原文相較有所修改)

DataTable GetDataFromExcelByCom(bool hasTitle = false) { OpenFileDialog openFile = new OpenFileDialog(); openFile.Filter = "Excel(*.xlsx)|*.xlsx|Excel(*.xls)|*.xls"; openFile.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); openFile.Multiselect = false; if (openFile.ShowDialog() == DialogResult.Cancel) return null; var excelFilePath = openFile.FileName; Excel.Application app = new Excel.Application(); Excel.Sheets sheets; object oMissiong = System.Reflection.Missing.Value; Excel.Workbook workbook = null; DataTable dt = new DataTable(); try { if (app == null) return null; workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong); sheets = workbook.Worksheets; //將數據讀入到DataTable中 Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//讀取第一張表 if (worksheet == null) return null; int iRowCount = worksheet.UsedRange.Rows.Count; int iColCount = worksheet.UsedRange.Columns.Count; //生成列頭 for (int i = 0; i < iColCount; i++) { var name = "column" + i; if (hasTitle) { var txt = ((Excel.Range)worksheet.Cells[1, i + 1]).Text.ToString(); if (!string.IsNullOrWhiteSpace(txt)) name = txt; } while (dt.Columns.Contains(name)) name = name + "_1";//重復行名稱會報錯。 dt.Columns.Add(new DataColumn(name, typeof(string))); } //生成行數據 Excel.Range range; int rowIdx = hasTitle ? 2 : 1; for (int iRow = rowIdx; iRow <= iRowCount; iRow++) { DataRow dr = dt.NewRow(); for (int iCol = 1; iCol <= iColCount; iCol++) { range = (Excel.Range)worksheet.Cells[iRow, iCol]; dr[iCol - 1] = (range.Value2 == null) ? "" : range.Text.ToString(); } dt.Rows.Add(dr); } return dt; } catch { return null; } finally { workbook.Close(false, oMissiong, oMissiong); System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook); workbook = null; app.Workbooks.Close(); app.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(app); app = null; } }
原文的方法二還提供了多線程處理數據的代碼,一並復制到此(此處出現了一個SheetOptions的類型,無法考證其來源,如果知曉,請留言,謝謝。):

/// <summary> /// 使用COM,多線程讀取Excel(1 主線程、4 副線程) /// </summary> /// <param name="excelFilePath">路徑</param> /// <returns>DataTabel</returns> public System.Data.DataTable ThreadReadExcel(string excelFilePath) { Excel.Application app = new Excel.Application(); Excel.Sheets sheets = null; Excel.Workbook workbook = null; object oMissiong = System.Reflection.Missing.Value; System.Data.DataTable dt = new System.Data.DataTable(); try { if (app == null) { return null; } workbook = app.Workbooks.Open(excelFilePath, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong, oMissiong); //將數據讀入到DataTable中——Start sheets = workbook.Worksheets; Excel.Worksheet worksheet = (Excel.Worksheet)sheets.get_Item(1);//讀取第一張表 if (worksheet == null) return null; string cellContent; int iRowCount = worksheet.UsedRange.Rows.Count; int iColCount = worksheet.UsedRange.Columns.Count; Excel.Range range; //負責列頭Start DataColumn dc; int ColumnID = 1; range = (Excel.Range)worksheet.Cells[1, 1]; //while (range.Text.ToString().Trim() != "") while (iColCount >= ColumnID) { dc = new DataColumn(); dc.DataType = System.Type.GetType("System.String"); string strNewColumnName = range.Text.ToString().Trim(); if (strNewColumnName.Length == 0) strNewColumnName = "_1"; //判斷列名是否重復 for (int i = 1; i < ColumnID; i++) { if (dt.Columns[i - 1].ColumnName == strNewColumnName) strNewColumnName = strNewColumnName + "_1"; } dc.ColumnName = strNewColumnName; dt.Columns.Add(dc); range = (Excel.Range)worksheet.Cells[1, ++ColumnID]; } //End //數據大於500條,使用多進程進行讀取數據 if (iRowCount - 1 > 500) { //開始多線程讀取數據 //新建線程 int b2 = (iRowCount - 1) / 10; DataTable dt1 = new DataTable("dt1"); dt1 = dt.Clone(); SheetOptions sheet1thread = new SheetOptions(worksheet, iColCount, 2, b2 + 1, dt1); Thread othread1 = new Thread(new ThreadStart(sheet1thread.SheetToDataTable)); othread1.Start(); //阻塞 1 毫秒,保證第一個讀取 dt1 Thread.Sleep(1); DataTable dt2 = new DataTable("dt2"); dt2 = dt.Clone(); SheetOptions sheet2thread = new SheetOptions(worksheet, iColCount, b2 + 2, b2 * 2 + 1, dt2); Thread othread2 = new Thread(new ThreadStart(sheet2thread.SheetToDataTable)); othread2.Start(); DataTable dt3 = new DataTable("dt3"); dt3 = dt.Clone(); SheetOptions sheet3thread = new SheetOptions(worksheet, iColCount, b2 * 2 + 2, b2 * 3 + 1, dt3); Thread othread3 = new Thread(new ThreadStart(sheet3thread.SheetToDataTable)); othread3.Start(); DataTable dt4 = new DataTable("dt4"); dt4 = dt.Clone(); SheetOptions sheet4thread = new SheetOptions(worksheet, iColCount, b2 * 3 + 2, b2 * 4 + 1, dt4); Thread othread4 = new Thread(new ThreadStart(sheet4thread.SheetToDataTable)); othread4.Start(); //主線程讀取剩余數據 for (int iRow = b2 * 4 + 2; iRow <= iRowCount; iRow++) { DataRow dr = dt.NewRow(); for (int iCol = 1; iCol <= iColCount; iCol++) { range = (Excel.Range)worksheet.Cells[iRow, iCol]; cellContent = (range.Value2 == null) ? "" : range.Text.ToString(); dr[iCol - 1] = cellContent; } dt.Rows.Add(dr); } othread1.Join(); othread2.Join(); othread3.Join(); othread4.Join(); //將多個線程讀取出來的數據追加至 dt1 后面 foreach (DataRow dr in dt.Rows) dt1.Rows.Add(dr.ItemArray); dt.Clear(); dt.Dispose(); foreach (DataRow dr in dt2.Rows) dt1.Rows.Add(dr.ItemArray); dt2.Clear(); dt2.Dispose(); foreach (DataRow dr in dt3.Rows) dt1.Rows.Add(dr.ItemArray); dt3.Clear(); dt3.Dispose(); foreach (DataRow dr in dt4.Rows) dt1.Rows.Add(dr.ItemArray); dt4.Clear(); dt4.Dispose(); return dt1; } else { for (int iRow = 2; iRow <= iRowCount; iRow++) { DataRow dr = dt.NewRow(); for (int iCol = 1; iCol <= iColCount; iCol++) { range = (Excel.Range)worksheet.Cells[iRow, iCol]; cellContent = (range.Value2 == null) ? "" : range.Text.ToString(); dr[iCol - 1] = cellContent; } dt.Rows.Add(dr); } } //將數據讀入到DataTable中——End return dt; } catch { return null; } finally { workbook.Close(false, oMissiong, oMissiong); System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook); System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets); workbook = null; app.Workbooks.Close(); app.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(app); app = null; GC.Collect(); GC.WaitForPendingFinalizers(); } }
補充SheetOptions代碼:

class SheetOptions { Microsoft.Office.Interop.Excel.Worksheet worksheet; int iColCount; int star; int end; System.Data.DataTable dt; public SheetOptions(Microsoft.Office.Interop.Excel.Worksheet worksheet, int iColCount, int star, int end, System.Data.DataTable dt) { this.worksheet = worksheet; this.iColCount = iColCount; this.star = star; this.end = end; this.dt = dt; } public void SheetToDataTable() { string cellContent; Microsoft.Office.Interop.Excel.Range range; for (int iRow = star; iRow <= end; iRow++) { System.Data.DataRow dr = dt.NewRow(); for (int iCol = 1; iCol <= iColCount; iCol++) { range = (Microsoft.Office.Interop.Excel.Range)worksheet.Cells[iRow, iCol]; cellContent = (range.Value2 == null) ? "" : range.Text.ToString(); dr[iCol - 1] = cellContent; } dt.Rows.Add(dr); } } }
原文還提供了第三種方法,感興趣的可以關心一下:
方法三:NPOI方式讀取Excel,NPOI是一組開源的組件,類似Java的 POI。包括:NPOI、NPOI.HPSF、NPOI.HSSF、NPOI.HSSF.UserModel、NPOI.POIFS、NPOI.Util,下載的時候別只下一個噢
優點:讀取Excel速度較快,讀取方式操作靈活性
缺點:只支持03的Excel,xlsx的無法讀取。由於這點,使用這種方式的人不多啊,沒理由要求客戶使用03版Excel吧,再說03版Excel對於行數還有限制,只支持65536行。
(聽他們的開發人員說會在2012年底推出新版,支持xlsx的讀取。但一直很忙沒時間去關注這個事情,有興趣的同學可以瞧瞧去)

1 using System; 2 using System.Data; 3 using System.IO; 4 using System.Web; 5 using NPOI; 6 using NPOI.HPSF; 7 using NPOI.HSSF; 8 using NPOI.HSSF.UserModel; 9 using NPOI.POIFS; 10 using NPOI.Util; 11 using System.Text; 12 using System.Configuration; 13 14 public class NPOIHelper 15 { 16 private static int ExcelMaxRow = Convert.ToInt32(ConfigurationManager.AppSettings["ExcelMaxRow"]); 17 /// <summary> 18 /// 由DataSet導出Excel 19 /// </summary> 20 /// <param name="sourceTable">要導出數據的DataTable</param> 21 /// <param name="sheetName">工作表名稱</param> 22 /// <returns>Excel工作表</returns> 23 private static Stream ExportDataSetToExcel(DataSet sourceDs) 24 { 25 HSSFWorkbook workbook = new HSSFWorkbook(); 26 MemoryStream ms = new MemoryStream(); 27 28 for (int i = 0; i < sourceDs.Tables.Count; i++) 29 { 30 HSSFSheet sheet = (HSSFSheet)workbook.CreateSheet(sourceDs.Tables[i].TableName); 31 HSSFRow headerRow = (HSSFRow)sheet.CreateRow(0); 32 // handling header. 33 foreach (DataColumn column in sourceDs.Tables[i].Columns) 34 headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName); 35 // handling value. 36 int rowIndex = 1; 37 foreach (DataRow row in sourceDs.Tables[i].Rows) 38 { 39 HSSFRow dataRow = (HSSFRow)sheet.CreateRow(rowIndex); 40 foreach (DataColumn column in sourceDs.Tables[i].Columns) 41 { 42 dataRow.CreateCell(column.Ordinal).SetCellValue(row[column].ToString()); 43 } 44 rowIndex++; 45 } 46 } 47 workbook.Write(ms); 48 ms.Flush(); 49 ms.Position = 0; 50 workbook = null; 51 return ms; 52 } 53 /// <summary> 54 /// 由DataSet導出Excel 55 /// </summary> 56 /// <param name="sourceTable">要導出數據的DataTable</param> 57 /// <param name="fileName">指定Excel工作表名稱</param> 58 /// <returns>Excel工作表</returns> 59 public static void ExportDataSetToExcel(DataSet sourceDs, string fileName) 60 { 61 //檢查是否有Table數量超過65325 62 for (int t = 0; t < sourceDs.Tables.Count; t++) 63 { 64 if (sourceDs.Tables[t].Rows.Count > ExcelMaxRow) 65 { 66 DataSet ds = GetdtGroup(sourceDs.Tables[t].Copy()); 67 sourceDs.Tables.RemoveAt(t); 68 //將得到的ds插入 sourceDs中 69 for (int g = 0; g < ds.Tables.Count; g++) 70 { 71 DataTable dt = ds.Tables[g].Copy(); 72 sourceDs.Tables.Add(dt); 73 } 74 t--; 75 } 76 } 77 78 MemoryStream ms = ExportDataSetToExcel(sourceDs) as MemoryStream; 79 HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment;filename=" + fileName); 80 HttpContext.Current.Response.BinaryWrite(ms.ToArray()); 81 HttpContext.Current.ApplicationInstance.CompleteRequest(); 82 //HttpContext.Current.Response.End(); 83 ms.Close(); 84 ms = null; 85 } 86 /// <summary> 87 /// 由DataTable導出Excel 88 /// </summary> 89 /// <param name="sourceTable">要導出數據的DataTable</param> 90 /// <returns>Excel工作表</returns> 91 private static Stream ExportDataTableToExcel(DataTable sourceTable) 92 { 93 HSSFWorkbook workbook = new HSSFWorkbook(); 94 MemoryStream ms = new MemoryStream(); 95 HSSFSheet sheet = (HSSFSheet)workbook.CreateSheet(sourceTable.TableName); 96 HSSFRow headerRow = (HSSFRow)sheet.CreateRow(0); 97 // handling header. 98 foreach (DataColumn column in sourceTable.Columns) 99 headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName); 100 // handling value. 101 int rowIndex = 1; 102 foreach (DataRow row in sourceTable.Rows) 103 { 104 HSSFRow dataRow = (HSSFRow)sheet.CreateRow(rowIndex); 105 foreach (DataColumn column in sourceTable.Columns) 106 { 107 dataRow.CreateCell(column.Ordinal).SetCellValue(row[column].ToString()); 108 } 109 rowIndex++; 110 } 111 workbook.Write(ms); 112 ms.Flush(); 113 ms.Position = 0; 114 sheet = null; 115 headerRow = null; 116 workbook = null; 117 return ms; 118 } 119 /// <summary> 120 /// 由DataTable導出Excel 121 /// </summary> 122 /// <param name="sourceTable">要導出數據的DataTable</param> 123 /// <param name="fileName">指定Excel工作表名稱</param> 124 /// <returns>Excel工作表</returns> 125 public static void ExportDataTableToExcel(DataTable sourceTable, string fileName) 126 { 127 //如數據超過65325則分成多個Table導出 128 if (sourceTable.Rows.Count > ExcelMaxRow) 129 { 130 DataSet ds = GetdtGroup(sourceTable); 131 //導出DataSet 132 ExportDataSetToExcel(ds, fileName); 133 } 134 else 135 { 136 MemoryStream ms = ExportDataTableToExcel(sourceTable) as MemoryStream; 137 HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment;filename=" + fileName); 138 HttpContext.Current.Response.BinaryWrite(ms.ToArray()); 139 HttpContext.Current.ApplicationInstance.CompleteRequest(); 140 //HttpContext.Current.Response.End(); 141 ms.Close(); 142 ms = null; 143 } 144 } 145 146 /// <summary> 147 /// 傳入行數超過65325的Table,返回DataSet 148 /// </summary> 149 /// <param name="dt"></param> 150 /// <returns></returns> 151 public static DataSet GetdtGroup(DataTable dt) 152 { 153 string tablename = dt.TableName; 154 155 DataSet ds = new DataSet(); 156 ds.Tables.Add(dt); 157 158 double n = dt.Rows.Count / Convert.ToDouble(ExcelMaxRow); 159 160 //創建表 161 for (int i = 1; i < n; i++) 162 { 163 DataTable dtAdd = dt.Clone(); 164 dtAdd.TableName = tablename + "_" + i.ToString(); 165 ds.Tables.Add(dtAdd); 166 } 167 168 //分解數據 169 for (int i = 1; i < ds.Tables.Count; i++) 170 { 171 //新表行數達到最大 或 基表數量不足 172 while (ds.Tables[i].Rows.Count != ExcelMaxRow && ds.Tables[0].Rows.Count != ExcelMaxRow) 173 { 174 ds.Tables[i].Rows.Add(ds.Tables[0].Rows[ExcelMaxRow].ItemArray); 175 ds.Tables[0].Rows.RemoveAt(ExcelMaxRow); 176 177 } 178 } 179 180 return ds; 181 } 182 183 /// <summary> 184 /// 由DataTable導出Excel 185 /// </summary> 186 /// <param name="sourceTable">要導出數據的DataTable</param> 187 /// <param name="fileName">指定Excel工作表名稱</param> 188 /// <returns>Excel工作表</returns> 189 public static void ExportDataTableToExcelModel(DataTable sourceTable, string modelpath, string modelName, string fileName, string sheetName) 190 { 191 int rowIndex = 2;//從第二行開始,因為前兩行是模板里面的內容 192 int colIndex = 0; 193 FileStream file = new FileStream(modelpath + modelName + ".xls", FileMode.Open, FileAccess.Read);//讀入excel模板 194 HSSFWorkbook hssfworkbook = new HSSFWorkbook(file); 195 HSSFSheet sheet1 = (HSSFSheet)hssfworkbook.GetSheet("Sheet1"); 196 sheet1.GetRow(0).GetCell(0).SetCellValue("excelTitle"); //設置表頭 197 foreach (DataRow row in sourceTable.Rows) 198 { //雙循環寫入sourceTable中的數據 199 rowIndex++; 200 colIndex = 0; 201 HSSFRow xlsrow = (HSSFRow)sheet1.CreateRow(rowIndex); 202 foreach (DataColumn col in sourceTable.Columns) 203 { 204 xlsrow.CreateCell(colIndex).SetCellValue(row[col.ColumnName].ToString()); 205 colIndex++; 206 } 207 } 208 sheet1.ForceFormulaRecalculation = true; 209 FileStream fileS = new FileStream(modelpath + fileName + ".xls", FileMode.Create);//保存 210 hssfworkbook.Write(fileS); 211 fileS.Close(); 212 file.Close(); 213 } 214 }