У меня проблема с памятью PHPExcel при обработке файла XLS.
Мне приходится работать с довольно большими файлами (от 50 до 200 тыс. Строк и 9-10 столбцов), поэтому мне пришлось использовать ReadFilters для решения проблемы с памятью.
Однако, хотя он довольно хорошо работал с файлами XLSX, используя смесь фоновых процессов и некоторые простые вычисления для размера чанка, я не могу заставить его работать с файлами XLS.
Это кусок кода, где все взрывается:
Class ExcelReadFilter implements PHPExcel_Reader_IReadFilter
{
private $startRow = 0;
private $endRow = 0;
public function setRows($startRow, $chunkSize) {
$this->startRow = $startRow;
$this->endRow = $startRow + $chunkSize;
}
public function readCell($column, $row, $worksheetName = '') {
if ( ($row >= $this->startRow && $row < $this->endRow) ) {
return true;
}
return false;
}
}
PHPExcel_Settings::setCacheStorageMethod( PHPExcel_CachedObjectStorageFactory::cache_in_memory_serialized );
....
$filter = new ExcelReadFilter();
$filter->setRows($desde, $cuantas);
$reader = PHPExcel_IOFactory::createReader($this->file_type);
$reader->setLoadSheetsOnly($sheet_name);
$reader->setReadDataOnly(false);
$reader->setReadFilter($filter);
$chunk = $reader->load($this->file);
$chunk->setActiveSheetIndexByName($sheet_name);
$active_sheet = $chunk->getActiveSheet();
$rowIterator = $active_sheet->getRowIterator($desde);
$this->num_filas = $active_sheet->getHighestRow();
Сразу после этого я включил следующие строки, чтобы лучше понять, что происходит:
ob_start();
var_dump($rowIterator);
$f = fopen("excel-info.txt", "w");
fwrite($f, ob_get_clean());
fclose($f);
ob_end_clean();
die;
И я думаю, что это указывало на то, где проблема с памятью.
Когда я впервые загрузил оригинальный файл XLS, файл excel-info.txt имел размер 13M.
Затем я открыл файл XLS и сохранил его как XLSX, и повторил процесс, после чего в excel-info.txt было всего 285 Кбайт.
Можно ли как-то изменить эти фильтры для работы с файлами XLS?
Да, и установка предела памяти PHP на более высокое значение не является опцией, но время выполнения не критично.
ADDED
Когда я использовал разные параметры кэширования памяти, я смог уменьшить использование памяти, достаточное для ее работы, и в большинстве случаев сохранил ее до приемлемого размера.
Прямо сейчас я использую PHPExcel_CachedObjectStorageFactory :: cache_to_sqlite, и, кажется, этого достаточно, чтобы он работал.
Хочу заметить, что мои расчеты над сериализованной информацией, помещенной в файл, были неверными.
Файлы Excel5 создают массив с таким количеством записей, сколько строк имеет файл excel, все его значения которого не соответствуют условию фильтра, установленному в NULL. Конечно, когда я сохраняю его в текстовый файл, что-то вроде …
array(10) {
["A"]=>
NULL
["B"]=>
NULL
["C"]=>
NULL
["D"]=>
NULL
["E"]=>
NULL
["F"]=>
NULL
["G"]=>
NULL
["H"]=>
NULL
["I"]=>
NULL
["J"]=>
NULL
}
…занимает много места в файле, но не в php_memory, так что это была моя ошибка.
Теперь я использую этот код для отслеживания использования памяти:
for ( $i=1; $i < 20000; $i+=5000 ){
$filter->setRows($i, 5000);
echo "\n1- Usage: ".(memory_get_usage ()/1024)/1024;
$objPHPExcel = $reader->load($this->file);
echo "\n2- Usage: ".(memory_get_usage ()/1024)/1024;
$sheetData = $objPHPExcel->getActiveSheet()->toArray(null,true,true,true);
unset($sheetData);
unset($objPHPExcel);
echo "\n3- Usage: ".(memory_get_usage ()/1024)/1024;
}
С определенным файлом XLS он показывает:
1-я итерация
1- Использование: 4.3859634399414
2- Использование: 34.292671203613
3- Использование: 34.68034362793
2-я итерация
1- Использование: 34.68034362793
2- Использование: 34.68293762207
3- Использование: 34.684982299805
И тот же файл, после сохранения как XLSX:
1-я итерация
1- Использование: 4.2780990600586
2- Использование: 6.9042129516602
3- Использование: 7.2916641235352
2-я итерация
1- Использование: 7.2916641235352
2- Использование: 7.5115432739258
3- Использование: 7.2813568115234
Однако я должен сказать, что после сохранения в формате XLSX размер уменьшается примерно вдвое, поэтому я не могу сказать, является ли это ошибкой или ожидаемым поведением.
PHPExcel это боров памяти. Я использовал его для нескольких клиентов и обнаружил, что вам нужно поэкспериментировать с настройкой лимита памяти php, чтобы найти подходящее место, где он может загрузить средний файл, который клиент может импортировать. Мне пришлось использовать целых 8 ГБ на некоторых проектах. Конечно, вы делаете это в подпрограмме, которая загружает файл xls, используя
ini_set (‘memory_limit’, ’16M’), а не в файле php.ini.
Вы пробовали setReadDataOnly (правда)?
Я думаю, что причина в том, что файлы xls — это не просто данные в формате csv, но и универсальная информация для множества другой информации (например, шрифтов и макросов). Когда вы загружаете файл, PHPExcel пытается загрузить все части в память, создавая огромную структуру.
Просто добавьте эту строку (например)
ini_set ( ‘memory_limit’, ‘254m’);
это решит проблему с памятью ..
не стесняйтесь изменять предел памяти, чтобы сделать его подходящим для вашего случая