¿Hay una manera de obtener los nombres de las hojas de los archivos de 20MB + Excel con PHPExcel?

StackOverflow https://stackoverflow.com/questions/4546110

  •  13-10-2019
  •  | 
  •  

Pregunta

Estoy utilizando PHPExcel para leer los datos de los archivos de Excel.

Con el siguiente código, soy capaz de leer una hoja de cálculo en particular de una archivo de 3 MB Excel en tan sólo un par de segundos. Funciona muy bien.

Sin embargo, ahora tengo 27MB y 88 MB archivos de Excel que necesito para obtener los datos de. Son tan grandes que incluso OpenOffice no puede abrirlos.

he encontrado que puedo usar un número de índice en lugar de un nombre cuando cargo una hoja, pero esto parece incompatible, por ejemplo en un detalle setLoadSheetsOnly(0) archivo de Excel me dio la tercer hoja, mientras que setLoadSheetsOnly(1) me dio un error a pesar de que había cuatro hojas de cálculo en el archivo. Por lo que parece poco fiable por alguna razón.

¿Hay alguna manera de leer los nombres de las hojas de trabajo de un archivo de gran tamaño para que pueda tener acceso a una sola de sus hojas de trabajo a la vez?

        $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;

Adición:

He encontrado un código que se acerca, pero no parece ser siempre exacta, por ejemplo, aquí se perdió la segunda hoja de cálculo en un archivo de 27 MB:

text alt

y aquí sólo se consiguió la tercera hoja de cálculo y se perdió otros 3:

text alt

$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;
¿Fue útil?

Solución

Por desgracia, no es posible leer los nombres de las hojas de trabajo sin tener que cargar todo el archivo.

Uso de un número de índice en lugar de un nombre al llamar setLoadSheetsOnly () no dará un resultado predecible: la lógica de código que realiza que los usos de verificación in_array () para probar si el sheetname que es punto de leer es en la matriz sheetnames de leer. por ejemplo.

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

Yo sospecharía que la comparación de una cadena contra un valor numérico va a dar un resultado verdadero de 0 == "mySheetName" al ejecutar esta prueba (basado en reglas de escritura y de fundición comparación sueltos de PHP).

Probablemente podría proporcionar un método lector que devuelve una lista de nombres de las hojas sin cargar el archivo completo, a pesar de que habría un impacto en el rendimiento implicado.

Editar

Si se agrega el siguiente método para Clases / PHPExcel / lector / 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;
}

Se le puede llamar mediante el uso de:

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

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

$ worksheetNames los vueltos debe contener un conjunto de todos los nombres de las hojas como UTF-8 cuerdas. Debido a que es sólo leer el mínimo absoluto de los .xlsx para recuperar estos nombres, que debe ser bastante rápido. Voy a hacer algunas pruebas más antes de registrarse en el PHPExcel SVN, pero (por ahora) parece hacer lo que necesita.

Edit2

Método equivalente para el lector 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;
}

No es tan eficiente como la versión Excel2007 lector, pero aún debe ser más rápido que analizar todo el archivo .xls sólo por los nombres de las hojas, porque sólo estoy analizaba el flujo global.

Otros consejos

Yo no quería modificar PHPExcel así que fui con esto:

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;
}

Sólo funciona en Excel 2007 o más tarde, pero hizo lo que necesitaba

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top