在内存中创建CSV文件,通过电子邮件发送,并从内存中删除。

16
private function convert_to_csv($input_array, $output_file_name, $delimiter) {

    $temp_memory = fopen('php://memory','w');

    foreach ($input_array as $line) {

        fputcsv($temp_memory, $line, $delimiter);

    }

    fseek($temp_memory, 0);

    header('Content-Type: application/csv');
    header('Content-Disposition: attachement; filename="' . $output_file_name . '";');

    fpassthru($temp_memory);

}               

我使用上述的函数将数据数组转换为CSV格式并输出到浏览器。有两个问题:

  1. 通过HTTP下载后,这个文件会从内存中删除吗?
  2. 如何重写相同的功能,使得这个文件可以被使用(例如作为PHPMailer发送的电子邮件附件),然后立即从内存中删除?

编辑:可工作代码-但是将内容写入文件而不是存储在内存中

public function emailCSVTest() {

    $test_array = array(array('Stuff','Yep'),array('More Stuff','Yep yep'));

    $temp_file = '/tmp/temptest.csv';

    $this->convertToCSV($test_array, $temp_file);

    $this->sendUserEmail('Test Subject','Test Message','nowhere@bigfurryblackhole.com',$temp_file);

    unlink($temp_file);

}

private function convertToCSV($input_array, $output_file) {

    $temp_file = fopen($output_file,'w');

    foreach ($input_array as $line) {

        fputcsv($temp_file, $line, ',');

    }

    fclose($temp_file);

}

仍未得到答复:原始函数是否从内存中删除文件?


1
我会将文件存储在文件系统上(例如/tmp),发送带附件的邮件,然后调用unlink()函数删除该文件。 - Niek van der Steen
所以将第一行更改为 $temp_file = fopen('/tmp/abc.csv','w'),创建文件,然后用 return $temp_file 替换 fseek >> fpassthru,在父函数中使用 unlink($temp_file) 进行跟进? - FurryWombat
像这样,是的。不要忘记先使用fclose()关闭文件。(在unlink()之前) - Niek van der Steen
谢谢!已经在上面添加了工作示例。 - FurryWombat
你为什么需要在第一时间将数据写入文件,如果你需要它是易失性的呢? - Mjh
好问题。答案不对,但对我的目的来说是有效的。下面选择的答案似乎更符合原始问题的要求。 - FurryWombat
1个回答

29

我会这样运用PHP的temp fopen包装器,再结合一定的内存阈值:

// we use a threshold of 1 MB (1024 * 1024), it's just an example
$fd = fopen('php://temp/maxmemory:1048576', 'w');
if ($fd === false) {
    die('Failed to open temporary file');
}

$headers = array('id', 'name', 'age', 'species');
$records = array(
    array('1', 'gise', '4', 'cat'),
    array('2', 'hek2mgl', '36', 'human')
);

fputcsv($fd, $headers);
foreach($records as $record) {
    fputcsv($fd, $record);
}

rewind($fd);
$csv = stream_get_contents($fd);
fclose($fd); // releases the memory (or tempfile)

内存阈值为1MB。如果CSV文件变得更大,PHP会创建一个临时文件,否则所有操作都将在内存中进行。好处是大型CSV文件不会耗尽内存。

关于您的第二个问题,fclose()会释放内存。

我曾经写过一篇关于这方面的博客文章,您可能会觉得有趣:http://www.metashock.de/2014/02/create-csv-file-in-memory-php/


如果我需要在其他函数中使用“file_path”(类似于CurlFile()),那么路径将是整个“php://temp/maxmemory:1048576”吗? - Andrew
不,那会打开一个新文件。你需要传递文件描述符 $fd。看起来这在 CurlFile() 中不起作用。这个函数的设计真的很糟糕。我建议提出一个功能请求,支持文件名或文件描述符(目前并不能真正帮助你,我知道)。 - hek2mgl
@Andrew 请查看这个答案:https://dev59.com/Y3M_5IYBdhLWcg3whznx#1342760。它展示了如何在内存中完成此操作。 - hek2mgl
或者我可以直接使用 $fd = tmpfile(); 吗?然后使用 stream_get_meta_data($fd)['uri'] 获取绝对路径。 - Andrew
我不想处理文件创建、唯一文件名保证、文件删除等问题,但我需要将数组“转换”为文件路径,因为CurlFile()接受文件路径(而另一端的API需要csv文件接收)。最好的方法是在内存中完成所有操作,但正如你自己所说 - 这是不可能的。 - Andrew
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接