PHPExcel заканчивается из 256, 512 и также 1024 МБ ОЗУ

Я этого не понимаю. Стол XSLX составляет около 3 МБ, но даже 1024 МБ ОЗУ недостаточно для того, чтобы PHPExcel загружал его в память?

Я мог бы сделать что-то ужасно неправильно здесь:

function ReadXlsxTableIntoArray($theFilePath) { require_once('PHPExcel/Classes/PHPExcel.php'); $inputFileType = 'Excel2007'; $objReader = PHPExcel_IOFactory::createReader($inputFileType); $objReader->setReadDataOnly(true); $objPHPExcel = $objReader->load($theFilePath); $rowIterator = $objPHPExcel->getActiveSheet()->getRowIterator(); $arrayData = $arrayOriginalColumnNames = $arrayColumnNames = array(); foreach($rowIterator as $row){ $cellIterator = $row->getCellIterator(); $cellIterator->setIterateOnlyExistingCells(false); // Loop all cells, even if it is not set if(1 == $row->getRowIndex ()) { foreach ($cellIterator as $cell) { $value = $cell->getCalculatedValue(); $arrayOriginalColumnNames[] = $value; // let's remove the diacritique $value = iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $value); // and white spaces $valueExploded = explode(' ', $value); $value = ''; // capitalize the first letter of each word foreach ($valueExploded as $word) { $value .= ucfirst($word); } $arrayColumnNames[] = $value; } continue; } else { $rowIndex = $row->getRowIndex(); reset($arrayColumnNames); foreach ($cellIterator as $cell) { $arrayData[$rowIndex][current($arrayColumnNames)] = $cell->getCalculatedValue(); next($arrayColumnNames); } } } return array($arrayOriginalColumnNames, $arrayColumnNames, $arrayData); } 

Вышеприведенная функция считывает данные из таблицы excel в массив.

Какие-либо предложения?

Сначала я разрешил PHP использовать 256 МБ ОЗУ. Этого было недостаточно. Затем я удвоил сумму, а затем попробовал 1024 МБ. Эта ошибка заканчивается:

 Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 50331648 bytes) in D:\data\o\WebLibThirdParty\src\PHPExcel\Classes\PHPExcel\Reader\Excel2007.php on line 688 Fatal error (shutdown): Allowed memory size of 1073741824 bytes exhausted (tried to allocate 50331648 bytes) in D:\data\o\WebLibThirdParty\src\PHPExcel\Classes\PHPExcel\Reader\Excel2007.php on line 688 

Было много написано об использовании памяти PHPExcel на форуме PHPExcel; поэтому чтение некоторых из этих предыдущих обсуждений может дать вам несколько идей. PHPExcel хранит «в памяти» представление электронной таблицы и подвержен ограничениям памяти PHP.

Физический размер файла в значительной степени не имеет значения … гораздо важнее знать, сколько ячеек (строк * столбцов на каждом рабочем листе) оно содержит.

«Эмпирическое правило», которое я всегда использовал, составляет в среднем около 1 к / ячейку, поэтому 5М-ячейке требуется 5 ГБ памяти. Однако есть несколько способов уменьшить это требование. Они могут быть объединены в зависимости от того, какую информацию вам нужно получить в своей книге, и что вы хотите с ней делать.

Если у вас несколько листов, но их не нужно загружать, вы можете ограничить листы, которые Reader загрузит, используя метод setLoadSheetsOnly (). Чтобы загрузить один и тот же рабочий лист:

 $inputFileType = 'Excel5'; $inputFileName = './sampleData/example1.xls'; $sheetname = 'Data Sheet #2'; /** Create a new Reader of the type defined in $inputFileType **/ $objReader = PHPExcel_IOFactory::createReader($inputFileType); /** Advise the Reader of which WorkSheets we want to load **/ $objReader->setLoadSheetsOnly($sheetname); /** Load $inputFileName to a PHPExcel Object **/ $objPHPExcel = $objReader->load($inputFileName); 

Или вы можете указать несколько листов с одним вызовом setLoadSheetsOnly (), передав массив имен:

 $inputFileType = 'Excel5'; $inputFileName = './sampleData/example1.xls'; $sheetnames = array('Data Sheet #1','Data Sheet #3'); /** Create a new Reader of the type defined in $inputFileType **/ $objReader = PHPExcel_IOFactory::createReader($inputFileType); /** Advise the Reader of which WorkSheets we want to load **/ $objReader->setLoadSheetsOnly($sheetnames); /** Load $inputFileName to a PHPExcel Object **/ $objPHPExcel = $objReader->load($inputFileName); 

Если вам нужно только получить доступ к части рабочего листа, вы можете определить фильтр чтения для определения того, какие ячейки вы хотите загрузить:

 $inputFileType = 'Excel5'; $inputFileName = './sampleData/example1.xls'; $sheetname = 'Data Sheet #3'; /** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */ class MyReadFilter implements PHPExcel_Reader_IReadFilter { public function readCell($column, $row, $worksheetName = '') { // Read rows 1 to 7 and columns A to E only if ($row >= 1 && $row <= 7) { if (in_array($column,range('A','E'))) { return true; } } return false; } } /** Create an Instance of our Read Filter **/ $filterSubset = new MyReadFilter(); /** Create a new Reader of the type defined in $inputFileType **/ $objReader = PHPExcel_IOFactory::createReader($inputFileType); /** Advise the Reader of which WorkSheets we want to load It's more efficient to limit sheet loading in this manner rather than coding it into a Read Filter **/ $objReader->setLoadSheetsOnly($sheetname); echo 'Loading Sheet using filter'; /** Tell the Reader that we want to use the Read Filter that we've Instantiated **/ $objReader->setReadFilter($filterSubset); /** Load only the rows and columns that match our filter from $inputFileName to a PHPExcel Object **/ $objPHPExcel = $objReader->load($inputFileName); 

Используя фильтры чтения, вы также можете прочитать книгу в «кусках», так что только один фрагмент является резидентной памятью в любой момент времени:

 $inputFileType = 'Excel5'; $inputFileName = './sampleData/example2.xls'; /** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */ 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; } } /** 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($inputFileName); // Do some processing here // Free up some of the memory $objPHPExcel->disconnectWorksheets(); unset($objPHPExcel); } с $inputFileType = 'Excel5'; $inputFileName = './sampleData/example2.xls'; /** Define a Read Filter class implementing PHPExcel_Reader_IReadFilter */ 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; } } /** 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($inputFileName); // Do some processing here // Free up some of the memory $objPHPExcel->disconnectWorksheets(); unset($objPHPExcel); } 

Если вам не нужно загружать информацию форматирования, а только данные рабочего листа, то метод setReadDataOnly () будет показывать читателю только на загрузку значений ячеек, игнорируя любое форматирование ячейки:

 $inputFileType = 'Excel5'; $inputFileName = './sampleData/example1.xls'; /** Create a new Reader of the type defined in $inputFileType **/ $objReader = PHPExcel_IOFactory::createReader($inputFileType); /** Advise the Reader that we only want to load cell data, not formatting **/ $objReader->setReadDataOnly(true); /** Load $inputFileName to a PHPExcel Object **/ $objPHPExcel = $objReader->load($inputFileName); 

Используйте кеширование клеток. Это метод сокращения памяти PHP, который требуется для каждой ячейки, но со скоростью в скорости. Он работает путем хранения объектов ячейки в сжатом формате или вне памяти PHP (например, диска, APC, memcache) … но чем больше памяти вы сохраняете, тем медленнее выполняются ваши скрипты. Тем не менее, вы можете уменьшить объем памяти, необходимый каждой ячейке, примерно до 300 байт, поэтому для гипотетических 5M-ячеек потребуется около 1,4 Гбайт памяти PHP.

Кэширование ячеек описано в разделе 4.2.1 Документации разработчика

РЕДАКТИРОВАТЬ

Если вы посмотрите на свой код, вы используете итераторы, которые не особенно эффективны и создают массив данных ячеек. Возможно, вы захотите посмотреть на метод toArray (), который уже встроен в PHPExcel, и делает это за вас. Также рассмотрим недавнее обсуждение SO о новом варианте метода rangeToArray () для создания ассоциативного массива данных строк.

У меня была проблема с памятью с PHPExcel и на самом деле все остальные библиотеки. Чтение данных в кусках, как предложил Марк Бейкер, может решить проблему (также работает кеширование), но оказалось, что проблема с памятью стала проблемой времени. Время чтения и записи было экспоненциальным, поэтому для больших электронных таблиц это было не очень удобно.

PHPExcel и другие не предназначены для обработки больших файлов, поэтому я создал библиотеку, которая решает эту проблему. Вы можете проверить это здесь: https://github.com/box/spout

Надеюсь, это поможет!

Есть много мер, которые вы можете предпринять, чтобы зарезервировать меньше памяти при работе с PHPExcel. Я рекомендую вам предпринять следующие действия для оптимизации использования памяти перед изменением лимита памяти сервера в Apache.

 /* Use the setReadDataOnly(true);*/ $objReader->setReadDataOnly(true); /*Load only Specific Sheets*/ $objReader->setLoadSheetsOnly( array("1", "6", "6-1", "6-2", "6-3", "6-4", "6-5", "6-6", "6-7", "6-8") ); /*Free memory when you are done with a file*/ $objPHPExcel->disconnectWorksheets(); unset($objPHPExcel); 

Избегайте использования очень больших файлов Exel, помните, что размер файла делает процесс медленным и аварийным.

Избегайте использования getCalculatedValue (); функция при чтении ячеек.

Ypu может попробовать PHP Excel http://ilia.ws/archives/237-PHP-Excel-Extension-0.9.1.html Его расширение C для php и его очень быстрое. (Также использует меньше памяти, чем реализации PHP)

В моем случае phpexcel всегда повторялся через 19999 строк. независимо от того, сколько строк действительно было заполнено. Таким образом, 100 строк данных всегда заканчивались ошибкой памяти.

Возможно, вам просто нужно проверить, если ячейки в текущей строке пустые, а затем «продолжить» или разбить цикл, который выполняет итерацию строк.

Просто перекладываю почту из другой темы. В нем описывается другой подход к серверу, который генерирует или редактирует электронные таблицы Excel, которые следует принимать во внимание. Для больших объемов данных я бы не рекомендовал такие инструменты, как PHPExcel или ApachePOI (для Java) из-за их требований к памяти. Существует еще один довольно удобный (хотя, может быть, немного странный) способ ввода данных в электронные таблицы. Таким образом, можно создавать серверы или обновлять электронные таблицы Excel, таким образом, простое редактирование XML. У вас может быть таблица XLSX, сидящая на сервере, и каждый раз, когда данные собираются с дБ, вы разархивируете ее с помощью php. Затем вы получаете доступ к определенным файлам XML, которые содержат содержимое рабочих листов, которые нужно вставлять, и вставлять данные вручную. Впоследствии вы сжимаете папку электронных таблиц, чтобы распространять ее как обычный файл XLSX. Весь процесс довольно быстрый и надежный. Очевидно, что существует несколько проблем и сбоев, связанных с внутренней организацией файла XLSX / Open XML (например, Excel имеет тенденцию хранить все строки в отдельной таблице и использовать ссылки на эту таблицу в файлах рабочих таблиц). Но при вводе только данных, таких как числа и строки, это не так сложно. Если кто-то заинтересован, я могу предоставить некоторый код.

Я столкнулся с этой проблемой, и, к сожалению, ни одно из предлагаемых решений не могло мне помочь. Мне нужна функциональность, предоставляемая PHPExcel (формулы, условный стиль и т. Д.), Поэтому использование другой библиотеки не было выбором.

То, что я в конце концов сделал, заключалось в том, чтобы написать каждый рабочий лист в отдельный (временный) файл, а затем объединить эти отдельные файлы с некоторым специальным программным обеспечением, которое я написал. Это уменьшило потребление памяти от> 512 Мб до 100 Мб. См. https://github.com/infostreams/excel-merge, если у вас есть та же проблема.