使用PHPExcel

时间:2015-05-18 13:03:10

标签: php performance phpexcel

我正在使用PHPExcel库从Excel文件中读取数据。我的文件大约是5mb,70列和20000行。加载文件的代码是:

     $sheetnames = array('Classification');
     $excelFile = Yii::app()->basePath . '/categories/'. $region .'.xlsx';
     $objReader = PHPExcel_IOFactory::createReader('Excel2007');
     $objReader->setReadDataOnly(true);
     $objReader->setLoadSheetsOnly($sheetnames);
     $objPHPExcel = $objReader->load($excelFile);

Excel文件具有以下结构:

Title | Id | Path | Attribute 1 | Attribute 2 | ... | Attribute 65

加载此文件大约需要6分钟,占用过多的CPU和RAM。 实际上,我需要知道具有给定ID的一行数据。现在我迭代所有行并检查id。这太低效了。

所以我有两个问题:

  1. 有没有办法更快地加载文件? (我不能用这么多时间,cpu和ram)
  2. 有没有办法更有效地搜索文件?

3 个答案:

答案 0 :(得分:5)

首先使用读取过滤器仅加载ID列:

/**  Define a Read Filter class implementing PHPExcel_Reader_IReadFilter  */ 
class SingleColumnFilter implements PHPExcel_Reader_IReadFilter 
{ 
    private $requestedColumn;

    public function __construct($column) {
        $this->requestedColumn = $column;
    }

    public function readCell($column, $row, $worksheetName = '') { 
        if ($column == $this->requestedColumn) { 
            return true; 
        } 
        return false; 
    } 
} 

/**  Create an Instance of our Read Filter  **/ 
$idColumnFilter = new SingleColumnFilter('B'); // Id is column B

$objReader = PHPExcel_IOFactory::createReader('Excel2007'); 
$objReader->setReadDataOnly(true);
$objReader->setLoadSheetsOnly($sheetnames);
/**  Tell the Reader that we want to use the Read Filter  **/ 
$objReader->setReadFilter($idColumnFilter); 
/**  Load only the column that matches our filter to PHPExcel  **/ 
$objPHPExcel = $objReader->load($inputFileName); 

然后,PHPExcel将仅加载列B中的单元格的数据。然后,您可以通过该子单元格搜索所需的值(1列和22,000行只有22,000个单元格,所以应该更接近35MB,而不是加载整个文件所需的2.5GB),然后使用类似的基于行号过滤以仅加载您已识别的单行。

修改

PHPExcel的最新1.8.1版本还有一个columnIterator,可以更容易地迭代列查找特定的ID值:

$found = false;
foreach ($objPHPExcel->getActiveSheet()->getColumnIterator('B') as $column) {
    $cellIterator = $column->getCellIterator();
    $cellIterator->setIterateOnlyExistingCells(true);
    foreach ($cellIterator as $key => $cell) {
        if ($cell->getValue == 'ABC') {
            $found = true;
            $rowId = $cell->getRow()
            break 2;
    }
}

编辑#2

一旦确定了所需的行,就可以使用第二个过滤器重新加载Excel文件......但只是那一行:

/**  Define a Read Filter class implementing PHPExcel_Reader_IReadFilter  */ 
class SingleRowFilter implements PHPExcel_Reader_IReadFilter 
{ 
    private $requestedRow;

    public function __construct($row) {
        $this->requestedRow = $row;
    }

    public function readCell($column, $row, $worksheetName = '') { 
        if ($row == $this->requestedRow) { 
            return true; 
        } 
        return false; 
    } 
} 

if ($found) {
    /**  Create an Instance of our Read Filter  **/ 
    $rowFilter = new SingleRowFilter($rowId);

    $objReader2 = PHPExcel_IOFactory::createReader('Excel2007'); 
    $objReader2->setReadDataOnly(true);
    $objReader2->setLoadSheetsOnly($sheetnames);
    /**  Tell the Reader that we want to use the Read Filter  **/ 
    $objReader2->setReadFilter($rowFilter); 
    /**  Load only the single row that matches our filter to PHPExcel  **/ 
    $objPHPExcel2 = $objReader2->load($inputFileName); 
}

答案 1 :(得分:2)

处理exel文件有点困难。只需使用shell exec将它们转换为CSV,并根据需要对这些CSV文件执行任何操作。

$ easy_install xlsx2csv
$ xlsx2csv file.xlsx newfile.csv

转换只需不到一秒钟。

答案 2 :(得分:0)

如果您想加速程序并减少内存消耗,可以查看Spout:https://github.com/box/spout

您需要做的就是:

$reader = ReaderFactory::create(Type::CSV);
$reader->open($filePath);

while ($reader->hasNextRow()) {
    $row = $reader->nextRow();
    $id = $row[1];
    // do stuff with the $id
}

$reader->close();

通过整个文件需要1到2秒:))