如何提高读取Excel的效率,NPOI多线程读取详解

如何提高读取Excel的效率

当Excel中的数据量大,达到几十万条,使用Excel文件可能会变得很慢. 以下是一些建议来提高读取Excel的效率:

1. 选择合适的Excel库

在读取Excel文件时, 选择合适的Excel库可以显著提高效率. 一些常用的Excel库包括:

2. 使用单元格范围

在读取Excel数据时, 不要逐行读取, 而是使用单元格范围. 这种方式可以减少对Excel文件的读取次数, 从而提高读取效率.

以下是一个使用单元格范围的示例:

using (var package = new ExcelPackage(new FileInfo(filePath)))
{
    ExcelWorksheet worksheet = package.Workbook.Worksheets[worksheetName];
    var range = worksheet.Cells[2, 1, 50000, 20]; // 选择2-50000行, 1-20列的单元格范围
    var data = range.Value; // 读取单元格范围中的数据
}

3. 使用多线程

使用多线程可以在读取Excel文件时提高效率. 例如, 可以使用一个线程读取Excel文件, 另一个线程处理数据.

以下是一个使用多线程的示例:

using (var package = new ExcelPackage(new FileInfo(filePath)))
{
    ExcelWorksheet worksheet = package.Workbook.Worksheets[worksheetName];
    var range = worksheet.Cells[2, 1, 50000, 20]; // 选择2-50000行, 1-20列的单元格范围

    // 使用多线程读取单元格范围中的数据
    var thread = new Thread(() => {
        var data = range.Value; // 读取单元格范围中的数据
        // 进行数据处理
    });
    thread.Start();
}

4. 关闭Excel应用程序

在读取Excel文件后, 要确保关闭Excel应用程序. 否则, Excel应用程序可能会保留在后台, 导致系统内存占用过高.

以下是一个关闭Excel应用程序的示例:

using (var package = new ExcelPackage(new FileInfo(filePath)))
{
    ExcelWorksheet worksheet = package.Workbook.Worksheets[worksheetName];
    var range = worksheet.Cells[2, 1, 50000, 20]; // 选择2-50000行, 1-20列的单元格范围
    var data = range.Value; // 读取单元格范围中的数据

    // 关闭Excel应用程序
    package.Dispose();
}

以上是一些改善读取Excel效率的建议. 根据Excel文件的大小和数据结构, 可以选择不同的方法来提高读取效率.

以下是使用NPOI多线程读取Excel的实例:

using System.Threading.Tasks;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

// ...

public void ReadExcelWithMultipleThreads(string filePath, string worksheetName)
{
    var workbook = new XSSFWorkbook(filePath);
    var worksheet = workbook.GetSheet(worksheetName);
    var range = new CellRangeAddress(1, worksheet.LastRowNum, 0, worksheet.GetRow(0).LastCellNum - 1); // 获取单元格范围

    // 将单元格范围中的数据分块, 每块1000行
    var chunks = range.Chunks(1000);
    var tasks = new List<Task>();

    foreach (var chunk in chunks)
    {
        var task = Task.Run(() =>
        {
            for (var i = chunk.FirstRow; i <= chunk.LastRow; i++)
            {
                var row = worksheet.GetRow(i);
                if (row == null) continue;

                for (var j = chunk.FirstColumn; j <= chunk.LastColumn; j++)
                {
                    var cell = row.GetCell(j);
                    if (cell == null) continue;

                    var cellValue = cell.ToString();
                    // 处理单元格数据
                }
            }
        });
        tasks.Add(task);
    }

    Task.WaitAll(tasks.ToArray());

    workbook.Close();
}

在此示例中, 使用CellRangeAddress.Chunks()方法将单元格范围分成多个块, 每个块包含1000行. 然后, 使用Task.Run()方法将每个块的读取操作分配给一个线程. 最后, 使用Task.WaitAll()等待所有线程完成后关闭Excel工作簿.

请注意, 在此示例中, 仅使用了一个工作表. 如果要同时读取多个工作表, 则需要根据需要进行修改.

CellRangeAddress.Chunks()方法在NPOI的2.5.x版本及以上版本中可以使用。

CellRangeAddress在NPOI中的命名空间为NPOI.SS.Util

感谢您的提醒,确实在NPOI的早期版本中没有CellRangeAddress.Chunks()方法。如果您使用的是早期版本,可以考虑手动分块,或者升级NPOI到2.5.x及以上版本。在NPOI 2.5.x及以上版本中,CellRangeAddress.Chunks()方法可以用于将单元格范围分成多个块,以便于在多线程环境下高效读取Excel文件。

另外,CellRangeAddress的命名空间是NPOI.SS.Util

以下是手动实现分块的代码,达到Chunks()方法的效果:

using System.Collections.Generic;
using NPOI.SS.Util;

// ...

public void ReadExcelManually(string filePath, string worksheetName)
{
    var workbook = new XSSFWorkbook(filePath);
    var worksheet = workbook.GetSheet(worksheetName);
    var range = new CellRangeAddress(1, worksheet.LastRowNum, 0, worksheet.GetRow(0).LastCellNum - 1); // 获取单元格范围
    var chunkSize = 1000; // 每个块的大小
    var chunks = new List<CellRangeAddress>();

    // 将单元格范围分成多个块, 每个块包含chunkSize行
    for (var i = range.FirstRow; i <= range.LastRow; i += chunkSize)
    {
        var firstRow = i;
        var lastRow = i + chunkSize - 1;
        if (lastRow > range.LastRow) lastRow = range.LastRow;
        chunks.Add(new CellRangeAddress(firstRow, lastRow, range.FirstColumn, range.LastColumn));
    }

    foreach (var chunk in chunks)
    {
        for (var i = chunk.FirstRow; i <= chunk.LastRow; i++)
        {
            var row = worksheet.GetRow(i);
            if (row == null) continue;

            for (var j = chunk.FirstColumn; j <= chunk.LastColumn; j++)
            {
                var cell = row.GetCell(j);
                if (cell == null) continue;

                var cellValue = cell.ToString();
                // 处理单元格数据
            }
        }
    }

    workbook.Close();
}

在此示例中,我们手动将单元格范围分成多个块,每个块包含1000行,然后使用嵌套循环逐个读取单元格,并在每个单元格上执行必要的操作。需要注意的是,手动分块可能需要更多的代码,但是它可以在NPOI的早期版本中使用。

不过,可能需要根据自己的具体情况进行调整。如果你的数据结构与示例代码不同,你可能需要修改块的大小或循环的索引。

猜你喜欢

转载自blog.csdn.net/Documentlv/article/details/130752174