2

使用下面的函数,我从表中提取行,对它们进行编码,然后将它们放入 csv 格式。我想知道是否有更简单的方法来防止高内存使用。我不想依赖 ini_set。我相信内存消耗是由于读取临时文件并将其压缩造成的。我希望能够使用 64mb 内存的限制。有任何想法吗?谢谢!

function exportcsv($tables) {
    foreach ($tables as $k => $v) {
        $fh = fopen("php://temp", 'w');
        $sql = mysql_query("SELECT * FROM $v");
        while ($row = mysql_fetch_row($sql)) {
            $line = array();
            foreach ($row as $key => $vv) {
                $line[] = base64_encode($vv);
            }
            fputcsv($fh, $line, chr(9));
        }
        rewind($fh);
        $data = stream_get_contents($fh);
        $gzdata = gzencode($data, 6);
        $fp = fopen('sql/'.$v.'.csv.gz', 'w');
        fwrite($fp, $gzdata);
        fclose($fp);
        fclose($fh);
    }
}
4

4 回答 4

2

未经测试,但希望你能理解

function exportcsv($tables) {
    foreach ($tables as $k => $v) {
        $fh = fopen('compress.zlib://sql/' .$v. '.csv.gz', 'w');
        $sql = mysql_unbuffered_query("SELECT * FROM $v");
        while ($row = mysql_fetch_row($sql)) {
            fputcsv($fh, array_map('base64_encode', $row), chr(9));
        }
        fclose($fh);
        mysql_free_result($sql);
    }
}

感兴趣的编辑点是mysql_unbuffered_query 的使用和php 压缩流的使用。常规 mysql_query() 将整个结果集缓冲到内存中。并且使用压缩流无需在写入文件之前将数据作为字符串再次缓冲到 php 内存中。

于 2011-11-25T19:38:43.953 回答
2

通过 将整个文件拉到内存stream_get_contents()中可能会杀死你。您不仅需要保存 base64 数据(通常比其原始内容多 33%),而且还需要处理 csv 开销。如果内存有问题,可以考虑简单地调用命令行 gzip 应用程序,而不是在 PHP 中进行 gzip,例如:

... database loop here ...
exec('gzip yourfile.csv');

而且您可能可以在 DB 循环中更好地优化事物,并就地编码,而不是为每一行构建一个新数组:

while($row = mysql_fetch_row($result)) {
   foreach ($row as $key => $val) {
       $row[$key] = base64_encode($val);
       fputcsv($fh, $row, chr(9));
   }
}

并不是说这会大大减少内存使用——它只是一行数据,所以除非你处理巨大的记录字段,否则它不会有太大的影响。

于 2011-11-25T19:29:20.460 回答
1

您可以在那里插入一些刷新,目前您的整个 php 文件将保存在内存中,然后在最后刷新,但是如果您手动

fflush($fh);

此外,您可以使用 gzip 逐行压缩整个文件,而不是 gzip

$gz = gzopen ( $fh, 'w9' );
gzwrite ( $gz, $content );
gzclose ( $gz );

这将逐行写入打包数据,而不是创建整个文件然后压缩它。

于 2011-11-25T19:33:35.753 回答
0

我在http://johnibanez.com/node/21上找到了以块压缩的建议

看起来根据您的目的进行修改并不难。

function gzcompressfile($source, $level = false){ 
    $dest = $source . '.gz'; 
    $mode = 'wb' . $level; 
    $error = false; 

    if ($fp_out = gzopen($dest, $mode)) { 
        if ($fp_in = fopen($source, 'rb')) { 
            while(!feof($fp_in)) {
                gzwrite($fp_out, fread($fp_in, 1024*512)); 
            }
            fclose($fp_in); 
        } 
        else
            $error=true; 

        gzclose($fp_out); 
    } 
    else 
        $error=true; 

    if ($error)
        return false; 
    else
        return $dest;
} 
于 2011-11-25T19:32:35.553 回答