fputcsv - 在创建较大文件期间内存不足

时间:2014-10-06 06:28:31

标签: php csv fputcsv

我有时会根据数据库信息创建大型csv文件供用户下载--100k或更多行。看来我在一些较大的文件上创建csv期间遇到内存问题。以下是我目前正在处理csv创建的示例。

这有什么办法吗?最初有32mb,并改为64mb,仍然有问题。

//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循环和fetch而不是foreach和fetchAll仍然会产生内存错误。

while($result = $log_stmt->fetch(PDO::FETCH_ASSOC))

如果我一次只加载一行,那怎么可能呢?

更新2:

我已使用memory_get_usage();

进一步跟踪到了while循环
echo (floor( memory_get_usage() / 1024) ).' kb<br />';

在while循环开始之前,结果为4658 kb,然后对于while循环的每次迭代,它每2-3次循环增加1kb,直到达到允许的32748 kb最大内存。

我该怎么做才能解决这个问题?

更新3:

今天玩的更多...这种方式对我来说没有多大意义 - 我只能假设这是一个奇怪的行为与php的GC。

方案1:我的查询获取所有80k行并使用while循环输出它们。获取查询后使用的内存大约为4500kb,然后在循环中每两到三行递增1kb。内存没有被发布,它在某些时候没有足够的内存就会崩溃。

while($results = $log_stmt->fetch(PDO::FETCH_ASSOC))
{
    echo $results['timestamp'].'<br/>';
}

方案2:我的查询现在循环播放,一次获得1000行,其中输出每行输出一个循环。内存最大值为400k,因为它循环并完成整个输出而没有内存问题。

对于这个例子,我只使用了80次计数器,因为我知道要检索的行数超过80k。实际上我显然必须做到这一点。

$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循环中没有新的变量,一切都被“重用”。完全相同的情况发生在另一个循环中的第二个场景中。

3 个答案:

答案 0 :(得分:1)

fetchAll获取所有记录,不仅查询它并使用fetch执行while循环,然后它不需要在内存中加载所有结果集。

http://php.net/manual/en/pdostatement.fetch.php

答案 1 :(得分:0)

然后我认为您应该尝试以位读取文件。读取它们并附加到一个csv文件中,这样就可以在此过程中释放内存。 您可以执行count(*),但尝试在多次收集之前找到总计数

答案 2 :(得分:-1)

我自己一直在使用php的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); ?>