C# 读取 Excel(大文件)

ASP.NET下C#读取Excel文件,有好几种方法,我了解到的有:Microsoft.Office.Interop.Excel.dll 、 Microsoft.Jet.OLEDB 、NPOI,其中NPOI应该是用的比较多的吧,我个人来说比较倾向使用NPOI,很方便。不过今天我的一个小伙伴突然微信我,说她现在的公司需要解析一个上百兆的Excel文件,使用NPOI会有内存溢出的问题,即使根据需求将文件大小控制在最小50M以内还是不行,问我有什么办法能解决这个问题。

这个问题虽然我没做深入的了解,但是按照经验来看很可能是NPOI的瓶颈,或者说是她用的这个版本NPOI版本的瓶颈。

那么这个问题怎么解决呢?上菜!

DocumentFormat.OpenXmlSDK

对,没错!就是他,微软提供的一个读取Excel的类库

1、通过NuGet搜索 DocumentFormat.OpenXml

我下载的是第二个,至于为啥是第二个,因为小.... 而且对.NetFramework版本没有依赖

2、解析Excel

 1         /// <summary>
 2         /// 获取Excel指定工作表数据
 3         /// </summary>
 4         /// <param name="filePath">Excel所在路径</param>
 5         /// <param name="sheetName">工作表名</param>
 6         /// <returns></returns>
 7         public static void GetExcelVlaue(string filePath, string sheetName)
 8         {
 9             //打开文件
10             SpreadsheetDocument document = SpreadsheetDocument.Open(filePath, false);
11             WorkbookPart workbook = document.WorkbookPart;
12             IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == sheetName);//此处改成读取第一个sheet页面即可
13             if (sheets.Count() == 0)
14             {
15                 //sheet空判断
16             }
17             WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheets.First().Id);
18             Worksheet worksheet = worksheetPart.Worksheet;
19             IEnumerable<Row> rows = worksheet.Descendants<Row>();
20             foreach (Row row in rows)//获取行的值
21             {
22                 foreach (Cell cell in row)
23                 {
24                     string columnValue = GetValue(cell, workbook.SharedStringTablePart);
25                 }
26             }
27         }
View Code
 1  /// <summary>
 2         /// 获取单元格信息  这也是官方获取值的方法
 3         /// </summary>
 4         /// <param name="cell"></param>
 5         /// <param name="stringTablePart">stringTablePart就是WorkbookPart.SharedStringTablePart,它存储了所有以SharedStringTable方式存储数据的子元素。</param>
 6         /// <returns></returns>
 7         public static string GetValue(Cell cell, SharedStringTablePart stringTablePart)
 8         {
 9             if (cell.ChildElements.Count == 0)
10                 return null;
11             //get cell value
12             String value = cell.CellValue.InnerText;
13             //Look up real value from shared string table
14             if ((cell.DataType != null) && (cell.DataType == CellValues.SharedString))
15                 value = stringTablePart.SharedStringTable
16                     .ChildElements[Int32.Parse(value)]
17                     .InnerText;
18             return value;
19         }
View Code 
亲测可用,没毛病!
400M左右的文件测试三次,数据量在50W条左右,耗时平均在00:01:10左右。
100M左右的文件测试一次,数据量在100W条左右,耗时00:03:47左右。
 1    public void Read()
 2         {
 3             DataTable dt = new DataTable();
 4             Stopwatch watch = new Stopwatch();
 5             watch.Start();
 6             using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(@"C:UsersAdministratorDesktop大数据.xlsx", false))
 7             {
 8                 WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
 9                 IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
10                 string relationshipId = sheets.First().Id.Value = sheets.First(x => x.Name == "Sheet1").Id.Value;
11                 WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
12                 Worksheet workSheet = worksheetPart.Worksheet;
13                 SheetData sheetData = workSheet.GetFirstChild<SheetData>();
14                 Row[] rows = sheetData.Descendants<Row>().ToArray();
15 
16                 int count = rows.Count();
17                 string time1 = watch.Elapsed.ToString();
18                 log.Error("excel解析完成,数据条数:" + count + ",耗时:" + time1);
19 
20                 watch.Restart();
21                 // 设置表头DataTable
22                 foreach (Cell cell in rows.ElementAt(0))
23                 {
24                     dt.Columns.Add((string)GetCellValue(spreadSheetDocument, cell));
25                 }
26 
27                 // 添加内容
28                 for (int rowIndex = 1; rowIndex < rows.Count(); rowIndex++)
29                 {
30                     DataRow tempRow = dt.NewRow();
31 
32                     for (int i = 0; i < rows[rowIndex].Descendants<Cell>().Count(); i++)
33                     {
34                         tempRow[i] = GetCellValue(spreadSheetDocument, rows[rowIndex].Descendants<Cell>().ElementAt(i));
35                     }
36                     dt.Rows.Add(tempRow);
37                 }
38 
39                 string time2 = watch.Elapsed.ToString();
40                 log.Error("data生成结束,耗时:" + time2);
41             }
42         }
View Code
 1  public static string GetCellValue(SpreadsheetDocument document, Cell cell)
 2         {
 3             SharedStringTablePart stringTablePart = document.WorkbookPart.SharedStringTablePart;
 4             string value = cell.CellValue.InnerXml;
 5 
 6             if (cell.DataType != null && (cell.DataType.Value == CellValues.SharedString || cell.DataType.Value == CellValues.String || cell.DataType.Value == CellValues.Number))
 7             {
 8                 return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText;
 9             }
10             else //浮点数和日期对应的cell.DataType都为NULL
11             {
12                 // DateTime.FromOADate((double.Parse(value)); 如果确定是日期就可以直接用过该方法转换为日期对象,可是无法确定DataType==NULL的时候这个CELL 数据到底是浮点型还是日期.(日期被自动转换为浮点
13                 return value;
14             }
15         }
View Code

感谢:

https://www.cnblogs.com/longshanshan/p/7156036.html

原文地址:https://www.cnblogs.com/PrintY/p/14002881.html