Я создаю иногда большие CSV-файлы из информации БД для пользователей, чтобы затем загрузить — 100 КБ или более строк. Похоже, я столкнулся с проблемой памяти во время создания CSV на некоторых из более крупных файлов. Вот пример того, как я в настоящее время занимаюсь созданием CSV.
Есть ли способ обойти это? Первоначально имел 32 МБ и изменил это до 64 МБ и до сих пор проблема.
//columns array
$log_columns = array(
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9'
);
//results from the db
$results = $log_stmt->fetchAll(PDO::FETCH_ASSOC);
$log_file = 'test.csv';
$log_path = $_SERVER['DOCUMENT_ROOT'].'/../user-data/'.$_SESSION['user']['account_id'].'/downloads/';
// if location does not exist create it
if(!file_exists($log_path))
{
mkdir($log_path, 0755, true);
}
// open file handler
$fp = fopen($log_path.$log_file, 'wb');
// write the csv column titles / labels
fputcsv($fp, $log_columns);
//are there any logs?
if($results)
{
//write the rows
foreach($results as $row)
{
//rows array
$log_rows = array(
$row['1'],
$row['2'],
$row['3'],
$row['4'],
$row['5'],
$row['6'],
$row['7'],
$row['8'],
$row['9']
);
//write the rows
$newcsv = fputcsv($fp, $log_rows);
}//end foreach
}
// there were no results so just return an empty log
else
{
$newcsv = fputcsv($fp, array('No results found.') );
}
//close handler
fclose($fp);
// if csv was created return true
if($newcsv)
{
return true;
}
ОБНОВИТЬ :
Использование цикла while и выборки вместо foreach и fetchAll все равно приводит к ошибке памяти.
while($result = $log_stmt->fetch(PDO::FETCH_ASSOC))
Как это возможно, если я загружаю только одну строку за раз?
ОБНОВЛЕНИЕ 2:
Я проследил это до цикла while, используя memory_get_usage();
echo (floor( memory_get_usage() / 1024) ).' kb<br />';
Перед запуском цикла while результат составляет 4658 КБ, а затем для каждой итерации цикла while он увеличивается на 1 КБ каждые 2-3 цикла, пока не достигнет максимально допустимого объема памяти 32748 КБ.
Что я могу сделать, чтобы решить эту проблему?
ОБНОВЛЕНИЕ 3:
Сегодня я больше поиграл с этим … то, как это работает, не имеет для меня особого смысла — я могу только предположить, что это странное поведение с php GC.
сценарий 1: Мой запрос получает все 80 тыс. Строк и использует цикл while для их вывода. Объем используемой памяти составляет около 4500 КБ после получения запроса, а затем увеличивается на 1 КБ каждые две-три строки, которые выводятся в цикле. Память не высвобождается как никогда, и в какой-то момент происходит сбой без достаточного количества памяти.
while($results = $log_stmt->fetch(PDO::FETCH_ASSOC))
{
echo $results['timestamp'].'<br/>';
}
сценарий 2: Мой запрос теперь зацикливается и получает 1000 строк за раз с циклом, который выводит каждую строку. Максимальная память составляет 400 Кб, поскольку она зацикливается и завершает весь вывод без проблем с памятью.
Для этого примера я просто использовал счетчик 80 раз, так как знаю, что нужно извлечь более 80 тыс. Строк. На самом деле мне бы пришлось сделать это иначе.
$t_counter = 0;
while($t_counter < 80)
{
//set bindings
$binding = array(
'cw_start' => $t_counter * 1000,
//some other bindings...
);
$log_stmt->execute($binding);
echo $t_counter.' after statement '.floor( memory_get_usage() / 1024 ).' kb<br />';
while($results = $log_stmt->fetch(PDO::FETCH_ASSOC))
{
echo $results['capture_timestamp'].'<br/>';
}
echo $t_counter.' after while'.floor( memory_get_usage() / 1024 ).' kb<br />';
$t_counter++;
}
Поэтому я предполагаю, что мой вопрос заключается в том, почему в первом сценарии увеличивается использование памяти, и ничего не выходит? В цикле while нет новых переменных, и все «используется повторно». Точно такая же ситуация происходит во втором сценарии просто внутри другого цикла.
fetchAll извлекает все записи, которые не просто запрашивают его и выполняют цикл while с fetch, тогда ему не нужно загружать весь набор результатов в память.
Тогда я думаю, что вы должны попробовать прочитать файлы в битах. Прочитайте их и добавьте в один CSV-файл, чтобы освободить память во время процесса.
Вы могли бы сделать подсчет (*), но попробуйте найти общее количество до многократного сбора
Я сам использую php’s csv, я даже использую его как систему базы данных (nosql)
пытаться
CSV-код для чтения
<?php
$CSVfp = fopen("filename.csv", "r");
if($CSVfp !== FALSE) {
$con=1;
while(! feof($CSVfp))
{
do something
}?>
**csv code for writting **
<?php
$list = array
(
"edmond,dog,cat,redonton",
"Glenn,Quagmire,Oslo,Norway",
);$file = fopen("filename.csv","w");foreach ($list as $line)
{fputcsv($file,explode(',',$line));}fclose($file); ?>