php导出百万数据生成csv 2024-05-10
2024-05-09 本文已影响0人
阿然学编程
//生成器减少内存占用
function chunk_iterator(iterable $iterable, int $size): Generator
{
if ($size > 0) {
$chunk = [];
$count = 0;
foreach ($iterable as $item) {
$chunk[] = $item;
$count++;
if ($count === $size) {
yield $chunk;
$chunk = [];
$count = 0;
}
}
if ($chunk) {
yield $chunk;
}
} else {
// 当 $size 为0时,直接逐个yield每个元素
foreach ($iterable as $item) {
yield $item;
}
}
}
function chunk_iterator(iterable $iterable, int $size): Generator
{
if ($size <= 0) {
foreach ($iterable as $item) {
yield $item;
}
return;
}
$chunk = [];
$count = 0;
foreach ($iterable as $item) {
$chunk[] = $item;
$count++;
if ($count === $size) {
yield $chunk;
$chunk = []; // 重置块,释放内存
$count = 0; // 重置计数器
}
}
// 处理最后不足 $size 的部分
if ($count > 0) {
yield $chunk;
}
}
function exportToCsv($filename, array $header, iterable $data, int $chunkSize = 10000)
{
// 设置合理的内存限制和执行时间限制
set_time_limit(0);
ini_set('memory_limit', '1024M');
// 配置响应头以便于文件下载
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="' . $filename . '.csv"');
header('Cache-Control: no-cache, max-age=0');
header('Pragma: no-cache');
// 使用输出流开始写入CSV
$handle = fopen('php://output', 'w');
// 决定是否写入UTF-8 BOM(根据实际需求调整)
fwrite($handle, "\xEF\xBB\xBF");
// 写入列标题
fputcsv($handle, $header);
$lineCount = 0;
// 分块写入数据并适时刷新缓冲区
foreach (iterator_to_array(chunk_iterator($data, $chunkSize)) as $chunk) {
foreach ($chunk as $item) {
foreach ($item as $k => $v) {
if (preg_match('/\d+/', $v) || is_numeric($v) && strlen($v) > 15) {
//数字或超过15位加"\t"防止数据失真乱码
$item[$k] = "\t" . $v;
}
}
if (fputcsv($handle, $item)) {
$lineCount++;
}
// 每达到指定行数刷新缓冲区
if ($lineCount % $chunkSize === 0) {
// 刷新输出缓冲,提高性能
ob_flush();
flush();
}
}
}
// 关闭输出流
fclose($handle);
// 正常结束脚本
exit();
}
- 使用示例
$data = Db::table('testhz')->field('配送员,配送区,日期')->select();
exportToCsv('test', ['日期', '配送区', '日期'], $data);