2016-04-14 11 views
6

im usando PHPExcel per leggere i file .xls. Ho poco tempo piuttosto che incontroLettura del file .xls tramite PHPExcel genera Errore fatale: dimensione della memoria consentita ... anche con il lettore chunk

Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 730624 bytes) in Excel\PHPExcel\Shared\OLERead.php on line 93 

dopo qualche googling, ho cercato chunkReader evitare questa (menzionato anche su PHPExcel homesite), ma ancora im stucked con questo errore.

Il mio pensiero è che, tramite lettore chunk, leggerò il file parte per parte e la mia memoria non sarà troppo piena. Ma ci deve essere qualche grave perdita di memoria? O stai liberando un pò di memoria cattiva? Ho anche provato ad aumentare la ram del server a 1 GB. La dimensione del file, che sto cercando di leggere è di circa 700k, che non è tanto (im anche leggendo ~ 20MB pdf, xlsx, docx, doc, etc file senza problemi). Quindi presumo ci possa essere solo un troll minore che ho trascurato.

codice simile a questo

function parseXLS($fileName){ 
    require_once dirname(__FILE__) . './sphider_design/include/Excel/PHPExcel/IOFactory.php'; 
    require_once dirname(__FILE__) . './sphider_design/include/Excel/PHPExcel/ChunkReadFilter.php'; 

    $inputFileType = 'Excel5'; 

    /** Create a new Reader of the type defined in $inputFileType **/ 
    $objReader = PHPExcel_IOFactory::createReader($inputFileType); 
    /** Define how many rows we want to read for each "chunk" **/ 
    $chunkSize = 20; 
    /** Create a new Instance of our Read Filter **/ 
    $chunkFilter = new chunkReadFilter(); 
    /** Tell the Reader that we want to use the Read Filter that we've Instantiated **/ 
    $objReader->setReadFilter($chunkFilter); 

    /** Loop to read our worksheet in "chunk size" blocks **/ 
    /** $startRow is set to 2 initially because we always read the headings in row #1 **/ 
    for ($startRow = 2; $startRow <= 65536; $startRow += $chunkSize) { 
     /** Tell the Read Filter, the limits on which rows we want to read this iteration **/ 
     $chunkFilter->setRows($startRow,$chunkSize); 
     /** Load only the rows that match our filter from $inputFileName to a PHPExcel Object **/ 
     $objPHPExcel = $objReader->load($fileName); 
     // Do some processing here 

     // Free up some of the memory 
     $objPHPExcel->disconnectWorksheets(); 
     unset($objPHPExcel); 
    } 
} 

E qui è il codice per chunkReader

class chunkReadFilter implements PHPExcel_Reader_IReadFilter 
{ 
    private $_startRow = 0; 
    private $_endRow = 0; 

    /** Set the list of rows that we want to read */ 
    public function setRows($startRow, $chunkSize) { 
     $this->_startRow = $startRow; 
     $this->_endRow  = $startRow + $chunkSize; 
    } 

    public function readCell($column, $row, $worksheetName = '') { 
     // Only read the heading row, and the rows that are configured in $this->_startRow and $this->_endRow 
     if (($row == 1) || ($row >= $this->_startRow && $row < $this->_endRow)) { 
      return true; 
     } 
     return false; 
    } 
} 

risposta

4

così ho trovato una soluzione interessante How to read large worksheets from large Excel files (27MB+) with PHPExcel?

come Addendum 3 in questione

edit1: anche con questa soluzione, sono venuto a collo di bottiglia con il mio messaggio errr preferito, ma ho trovato qualcosa sulla memorizzazione nella cache, così ho implementato questo

$cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_phpTemp; 
$cacheSettings = array(' memoryCacheSize ' => '8MB'); 
PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); 

recentemente ho provato solo per i file XLS inferiori a 10 MB, ma sembra che per il lavoro (anche ho impostato $objReader->setReadDataOnly(true);) e sembra abbastanza equilibrata per raggiungere la velocità e la memoria co nsumption. (seguirò di più il mio percorso spinoso, se possibile)

edit2: Così ho fatto ulteriori ricerche e ho scoperto che il lettore di chunk non era necessario a modo mio. (mi sembra, il problema della memoria è lo stesso con il chunk reader e senza di esso.) Quindi la mia risposta finale alla mia domanda è qualcosa del genere, che legge il file .xls (solo dati dalle celle, senza formattare, anche filtrando le formule). Quando uso cache_tp_php_temp im grado di leggere file xls (testato a 10 MB) e circa 10k righe e colonne multiple in pochi secondi e senza problema di memoria

function parseXLS($fileName){ 

/** PHPExcel_IOFactory */ 
    require_once dirname(__FILE__) . './sphider_design/include/Excel/PHPExcel/IOFactory.php'; 
    require_once dirname(__FILE__) . './sphider_design/include/Excel/PHPExcel/ChunkReadFilter.php'; 
    require_once dirname(__FILE__) . './sphider_design/include/Excel/PHPExcel.php'; 

    $inputFileName = $fileName; 
    $fileContent = ""; 

    //get inputFileType (most of time Excel5) 
    $inputFileType = PHPExcel_IOFactory::identify($inputFileName); 

    //initialize cache, so the phpExcel will not throw memory overflow 
    $cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_phpTemp; 
    $cacheSettings = array(' memoryCacheSize ' => '8MB'); 
    PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); 

    //initialize object reader by file type 
    $objReader = PHPExcel_IOFactory::createReader($inputFileType); 

    //read only data (without formating) for memory and time performance 
    $objReader->setReadDataOnly(true); 

    //load file into PHPExcel object 
    $objPHPExcel = $objReader->load($inputFileName); 

    //get worksheetIterator, so we can loop sheets in workbook 
    $worksheetIterator = $objPHPExcel->getWorksheetIterator(); 

    //loop all sheets 
    foreach ($worksheetIterator as $worksheet) {  

      //use worksheet rowIterator, to get content of each row 
      foreach ($worksheet->getRowIterator() as $row) { 
       //use cell iterator, to get content of each cell in row 
       $cellIterator = $row->getCellIterator(); 
       //dunno 
       $cellIterator->setIterateOnlyExistingCells(false);  

       //iterate each cell 
       foreach ($cellIterator as $cell) { 
        //check if cell exists 
        if (!is_null($cell)) { 
         //get raw value (without formating, and all unnecessary trash) 
         $rawValue = $cell->getValue(); 
         //if cell isnt empty, print its value 
         if ((trim($rawValue) <> "") and (substr(trim($rawValue),0,1) <> "=")){ 
          $fileContent .= $rawValue . " ";            
         } 
        } 
       }  
      }  
    } 

    return $fileContent; 
} 
0

qui è quello che ho fatto, sulla base di esempi. Ho scoperto che alcune variabili con il motore php devono essere impostate per garantire il successo della funzione. Guarda questo. Rimuovo alcune parti da inserire nel mio database ma l'idea principale è qui.

$upload_dir = dirname(__DIR__) . "/uploads/"; 
$inputFileName = $upload_dir . basename($_FILES["fileToUpload"]["name"]); 
$insertOk = FALSE; 

// get inputFileType (most of time Excel5) 
$inputFileType = PHPExcel_IOFactory::identify($inputFileName); 

// initialize cache, so the phpExcel will not throw memory overflow 
ini_set('memory_limit', '-1'); 
ini_set('max_execution_time', 180); // 180 seconds of execution time maximum 
$cacheMethod = PHPExcel_CachedObjectStorageFactory::cache_to_phpTemp; 
$cacheSettings = array(' memoryCacheSize ' => '8MB'); 
PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings); 

// initialize object reader by file type 
$objReader = PHPExcel_IOFactory::createReader($inputFileType); 

// read only data (without formating) for memory and time performance 
$objReader->setReadDataOnly(true); 

// load file into PHPExcel object 
$objPHPExcel = $objReader->load($inputFileName); 
$objPHPExcel->setActiveSheetIndex(0); 

$spreadsheetInfo = $objReader->listWorksheetInfo($inputFileName); 
$maxRowsAllowed = $spreadsheetInfo[0]['totalRows']; 

// Define how many rows we want to read for each "chunk" 
$chunkSize = 200; 

// Create a new Instance of our Read Filter 
$chunkFilter = new ReportChunkReadFilter(); 

// Tell the Reader that we want to use the Read Filter that we've 
// Instantiated 
$objReader->setReadFilter($chunkFilter); 

// Loop to read our worksheet in "chunk size" blocks 
for ($startRow = 0; $startRow <= $maxRowsAllowed; $startRow += $chunkSize) { 
    // Tell the Read Filter, the limits on which rows we want to 
    // read this iteration 
    $chunkFilter->setRows($startRow,$chunkSize); 

    // Load only the rows that match our filter from $inputFileName 
    // to a PHPExcel Object 
    $objPHPExcel = $objReader->load($inputFileName); 
    $sheetData = $objPHPExcel->getActiveSheet()->toArray(null,true,true,true); 

    // loop on the rows of the filtered excel file (the chunk) 
    foreach ($sheetData as $rowArray) {          
     echo $rowArray['A']; 
     // do your stuff here 
    } 

    // Free up some of the memory 
    $objPHPExcel->disconnectWorksheets(); 
    unset($objPHPExcel);      
} 

unlink($inputFileName); 
Problemi correlati