导出当前页数据必须剥离分页逻辑,复用相同业务查询条件但去除limit/offset;需独立接口或签名校验防止get参数污染;csv导出应流式写入并设utf-8响应头;excel导出成本高,优先选用csv。

导出当前页数据前必须剥离分页逻辑
PHP分页(如用 limit + offset)本质是查询截断,导出时若直接复用分页SQL,会只导出当前页那十几条——这不是“导出当前页”,而是“误以为导出当前页”。真正要做的,是复用**同一份业务查询条件**,但去掉 LIMIT 和 OFFSET,重新查全量匹配数据(或按需加限制,比如最多导出1万条)。
常见错误:把 $_GET['page'] 和 $_GET['per_page'] 直接塞进导出逻辑,结果导出文件里只有20行。
- 从控制器/路由入口判断是否为导出请求(例如检测
$_GET['export'] === '1'),而非依赖分页参数 - 构造查询时,先拼好
WHERE条件部分(含搜索、状态筛选等),再根据请求类型决定是否追加LIMIT - 导出前对数据量做兜底检查,比如
if ($total > 10000) { die('导出数量超限'); }
用 fputcsv 写 CSV 时注意中文和内存控制
直接 implode(',', $row) 拼接中文字段会乱码,且大数组 array_map('str_getcsv', $data) 容易内存溢出。正确做法是边查边写,用 fputcsv() 流式输出。
关键点:
立即学习“PHP免费学习笔记(深入)”;
- 响应头必须设为
Content-Type: text/csv; charset=utf-8,并加Content-Disposition: attachment; filename="export.csv" - 打开输出流用
$fp = fopen('php://output', 'w'),不要用临时文件再读取 - 每查一批(如500行),调用一次
foreach ($batch as $row) { fputcsv($fp, $row, ',', '"'); },避免单次加载全部数据到内存 - 中文字段无需手动
mb_convert_encoding(),只要确保数据库连接、PHP脚本、输出流三者编码都是UTF-8
$_GET 参数污染导致导出内容与当前页不一致
用户在分页列表页点击“导出当前页”时,URL里带着 page=3&per_page=20&keyword=abc,但如果导出接口没严格校验参数来源,可能被篡改成 page=1&per_page=10000&keyword=,结果导出的是第1页+全词模糊匹配的数据——表面是“当前页”,实际已偏移。
解决方式很直接:
- 导出操作应走独立接口(如
/export?filter_id=123),filter_id对应服务端缓存的查询快照(含完整 WHERE 条件序列化),而不是透传所有$_GET - 若必须用 GET 参数,导出前用
hash_hmac('sha256', $raw_params, $secret)校验签名,防止篡改 - 禁止在导出逻辑里读取
$_GET['page']或$_GET['per_page']——它们只对分页渲染有意义
Excel 导出比 CSV 多出的三个硬性成本
用 PhpSpreadsheet 导出 Excel 看似更友好,但会显著抬高实现复杂度和运行风险:
- 内存占用翻3–5倍:CSV 导出1万行约占用20MB,Excel 可能飙到100MB+,容易触发
Allowed memory size exhausted - 无法流式写入:必须构建完整对象后一次性
->save(),中间不能ob_flush(),超时风险高 - 中文样式需额外处理:字体设为
SimSun或Microsoft YaHei,否则默认显示方块;日期列需显式设NumberFormat,否则变数字串
除非业务明确要求单元格合并、公式、颜色标记,否则优先用 CSV。Excel 需求强烈时,建议用命令行工具(如 csvkit)在后台异步转换,不阻塞PHP进程。











