在PHP代码中想要将数据导出到浏览器,如下:持续下载
利用php://output
输出流,以及fputcsv
函数,yield
语法糖
public function export()
{
$regions = RegionModel::find();
Csv::export((function (ActiveQuery $regions) {
//yield ['ID', '名称', '编号',];//表头
foreach ($regions->asArray()->each() as $region) {
yield $region;
}
})($regions));
}
类Csv如下:
class Csv
{
/**
* @param \Generator|array $data
* @param string $filename
*/
public static function export($data, string $filename = '')
{
self::_setHeader($filename ?: date('YmdHis'));
self::_putCsv($data);
exit;
}
/**
* @param string $filename
*/
private static function _setHeader(string $filename)
{
header('Content-Encoding: UTF-8');
header('Content-Type: text/csv;charset=UTF-8');
header("Content-Disposition: attachment;filename=\"{$filename}.csv\"");
header('Cache-Control: no-cache, private');
header('Date: ' . gmdate('D, d M Y H:i:s') . ' GMT');
}
/**
* @param \Generator|array $data
*/
private static function _putCsv($data)
{
$handle = fopen('php://output', 'w');
//导出的CSV文件是无BOM编码UTF-8,而我们通常使用UTF-8编码格式都是有BOM的。所以添加BOM于CSV中
fwrite($handle, chr(0xEF) . chr(0xBB) . chr(0xBF));
foreach ($data as $datum) {
fputcsv($handle, $datum);
}
fclose($handle);
}
}
参数$data
可以是数组,也可以是生成器。
使用yield
进行迭代避免了内存溢出问题,数据量大也没问题了。
试了一下,下载一个33M的文件,内存占用2M。