有没有办法从PHPExcel获取20MB + Excel文件的工作表名称?

时间:2010-12-28 12:42:36

标签: php phpexcel large-files

我正在使用PHPExcel从Excel文件中读取数据。

使用以下代码,我可以在几秒钟内从 3MB Excel文件中读取一个特定工作表。很好地工作。

但是,我现在有 27MB 88MB Excel文件,我需要从中获取数据。它们非常大,即使 OpenOffice 也无法打开它们。

我发现在加载工作表时我可以使用索引号而不是名称,但这似乎不一致,例如在一个特定的Excel文件中setLoadSheetsOnly(0)给了我第三个表,而setLoadSheetsOnly(1)给了我错误,即使文件中有四个工作表。因此出于某种原因,似乎不可靠

有没有办法可以从大文件中读出工作表的名称,这样我一次只能访问其中一个工作表?

        $objReader = PHPExcel_IOFactory::createReaderForFile("data/" . $file_name);
        $objReader->setLoadSheetsOnly(array($sheet_name));
        $objReader->setReadDataOnly(true);
        $objPHPExcel = $objReader->load("data/" . $file_name);

        echo '<table border="1">';
        for ($row = 1; $row < $number_of_rows; $row++) {
            echo '<tr>';
            for ($column = 0; $column < $number_of_columns; $column++) {
                $value = $objPHPExcel->setActiveSheetIndex(0)->getCellByColumnAndRow($column, $row)->getValue();
                echo '<td>';
                echo $value . '&nbsp;';
                echo '</td>';
            }
            echo '</tr>';
        }
        echo '</table>';
        die;

附录:

我找到了一些接近的代码,但它似乎并不总是准确的,例如这里错过了27MB文件中的第二个工作表:

alt text

在这里它只获得了第三张工作表而错过了其他3张:

alt text

$objReader = PHPExcel_IOFactory::createReaderForFile("data/" . $file_name);
$objReader->setLoadSheetsOnly(0);
$objReader->setReadDataOnly(true);
$objPHPExcel = $objReader->load("data/" . $file_name);

echo $objPHPExcel->getSheetCount(), ' worksheets<hr/>';
$loadedSheetNames = $objPHPExcel->getSheetNames();
foreach ($loadedSheetNames as $sheetIndex => $loadedSheetName) {
    echo $sheetIndex, ' -> ', $loadedSheetName, '<br />';
}
die;

2 个答案:

答案 0 :(得分:3)

不幸的是,如果不加载整个文件,就无法读取工作表的名称。

调用setLoadSheetsOnly()时使用索引号而不是名称将不会产生可预测的结果:执行该检查的代码逻辑使用in_array()来测试它将要读取的表名是否在数组中要读取的片材名称。 e.g。

// check if sheet should be skipped
if (isset($this->_loadSheetsOnly) && !in_array($sheet['name'], $this->_loadSheetsOnly)) {
    continue;
}

我怀疑在执行此测试时(基于PHP的松散类型和比较转换规则),字符串与数值的比较将给出0 ==“mySheetName”的真实结果。

我可能会提供一个Reader方法,它会返回一个工作表名称列表而不会实际加载整个文件,尽管会涉及性能损失。

修改

如果将以下方法添加到Classes / PHPExcel / Reader / Excel2007.php

/**
 * Reads names of the worksheets from a file, without loading the whole file to a PHPExcel object
 *
 * @param   string      $pFilename
 * @throws  Exception
 */
public function listWorksheetNames($pFilename)
{
    // Check if file exists
    if (!file_exists($pFilename)) {
        throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
    }

    $worksheetNames = array();

    $zip = new ZipArchive;
    $zip->open($pFilename);

    $rels = simplexml_load_string($this->_getFromZipArchive($zip, "_rels/.rels")); //~ http://schemas.openxmlformats.org/package/2006/relationships");
    foreach ($rels->Relationship as $rel) {
        switch ($rel["Type"]) {
            case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument":
                $xmlWorkbook = simplexml_load_string($this->_getFromZipArchive($zip, "{$rel['Target']}"));  //~ http://schemas.openxmlformats.org/spreadsheetml/2006/main");

                if ($xmlWorkbook->sheets) {
                    foreach ($xmlWorkbook->sheets->sheet as $eleSheet) {
                        // Check if sheet should be skipped
                        $worksheetNames[] = (string) $eleSheet["name"];
                    }
                }
        }
    }

    $zip->close();

    return $worksheetNames;
}

您可以使用以下方式调用它:

$inputFileType = 'Excel2007';
$inputFileName = 'biostat-behfisk-2005.xlsx';
$objReader = PHPExcel_IOFactory::createReader($inputFileType);
$worksheetNames = $objReader->listWorksheetNames($inputFileName);

foreach ($worksheetNames as $sheetName) {
    echo $sheetName, '<br />';
}

返回的$ worksheetNames应包含所有工作表名称的数组作为UTF-8字符串。因为它只是从.xlsx读取绝对最小值来检索这些名称,所以它应该相当快。在将其检入PHPExcel SVN之前,我会做更多测试,但(现在)它似乎可以满足您的需求。

<强> EDIT2

Excel5阅读器的等效方法

/**
 * Reads names of the worksheets from a file, without loading the whole file to a PHPExcel object
 *
 * @param   string      $pFilename
 * @throws  Exception
 */
public function listWorksheetNames($pFilename)
{
    // Check if file exists
    if (!file_exists($pFilename)) {
        throw new Exception("Could not open " . $pFilename . " for reading! File does not exist.");
    }

    $worksheetNames = array();

    // Read the OLE file
    $this->_loadOLE($pFilename);

    // total byte size of Excel data (workbook global substream + sheet substreams)
    $this->_dataSize = strlen($this->_data);

    $this->_pos     = 0;
    $this->_sheets  = array();

    // Parse Workbook Global Substream
    while ($this->_pos < $this->_dataSize) {
        $code = self::_GetInt2d($this->_data, $this->_pos);

        switch ($code) {
            case self::XLS_Type_BOF:    $this->_readBof();      break;
            case self::XLS_Type_SHEET:  $this->_readSheet();    break;
            case self::XLS_Type_EOF:    $this->_readDefault();  break 2;
            default:                    $this->_readDefault();  break;
        }
    }

    foreach ($this->_sheets as $sheet) {
        if ($sheet['sheetType'] != 0x00) {
            // 0x00: Worksheet, 0x02: Chart, 0x06: Visual Basic module
            continue;
        }

        $worksheetNames[] = $sheet['name'];
    }

    return $worksheetNames;
}

不如Excel2007 Reader版本那么高效,但仍然比仅仅为表格名称解析整个.xls文件更快,因为我只解析全局流。

答案 1 :(得分:2)

我不想修改phpexcel,所以我选择了这个:

public function getWorksheetNames($pFilename) {

    $worksheetNames = array ();

    $zip = zip_open ( $pFilename );
    while ( $entry = zip_read ( $zip ) ) {

        $entry_name = zip_entry_name ( $entry );
        if ($entry_name == 'xl/workbook.xml') {
            if (zip_entry_open ( $zip, $entry, "r" )) {
                $buf = zip_entry_read ( $entry, zip_entry_filesize ( $entry ) );
                $workbook = simplexml_load_string ( $buf );
                foreach ( $workbook->sheets as $sheets ) {
                    foreach( $sheets as $sheet) {
                        $attributes=$sheet->attributes();
                        $worksheetNames[]=$attributes['name'];
                    }
                }
                zip_entry_close ( $entry );
            }
            break;
        }

    }
    zip_close ( $zip );
    return $worksheetNames;
}

它仅适用于excel 2007或更高版本但是我需要的是