跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

如何实现PHP批量数据导入导出

核心问题是CSV/Excel编码与PHP解析不匹配:Windows下Excel默认UTF-8 with BOM,fgetcsv不自动剥离BOM导致首列乱码;PhpSpreadsheet需显式设setReadDataOnly(true)和setLoadSheetsOnly()防OOM,.xls文件须用Excel5 reader。 PHP 导入 Excel 文件时中文乱码怎么解决 核心问题不是 PHP 本身,而是
fgetcsv
、
str_getcsv
或第三方库(如 PhpSpreadsheet)读取时没正确识别 BOM 和编码。Windows 下 Excel 默认保存为 UTF-8 with BOM,但 PHP 的
fopen
+
fgetcsv
不会自动剥离 BOM,导致第一列字段名或首行数据开头多出不可见字符,后续
mb_convert_encoding
处理失效。 实操建议: 立即学习 “ PHP免费学习笔记(深入) ”; 用
file_get_contents
先读文件头 3 字节,判断是否为
\xEF\xBB\xBF
(UTF-8 BOM),是则用
substr($content, 3)
截掉再写回临时文件或传给
str_getcsv
PhpSpreadsheet 中必须显式设置读取编码:
$reader->setReadDataOnly(true); $reader->setLoadAllSheets();
,并确保
IOFactory::load()
前不手动
mb_internal_encoding('GBK')
—— 它内部已按 Excel 自带编码解析 若上传的是 .xls(非 .xlsx),别用
XmlReader
方式加载,改用
Excel5
reader,否则中文字段名直接变空 用 PhpSpreadsheet 导出万级数据内存爆掉怎么办 默认方式是「全量加载 → 写入内存 → 一次性输出」,1 万行 × 20 列的 Excel 就可能吃掉 200MB+ 内存。关键不是换库,而是切换到「流式写入」模式。 实操建议: 立即学习 “ PHP免费学习笔记(深入) ”; 不用
Spreadsheet
实例,改用
Writer\Xlsx\Writer
+
Worksheet
的流式接口:
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Writer($spreadsheet);
不行,得用
\PhpOffice\PhpSpreadsheet\Writer\Xlsx
并配合
setPreCalculateFormulas(false)
逐行写入:用
$sheet->fromArray($row, NULL, "A{$currentRow}")
替代
$sheet->setCellValue()
单元格赋值,减少对象创建开销 导出前调用
$spreadsheet->garbageCollect()
,尤其在循环中每写 1000 行后执行一次 避免在循环里调用
$spreadsheet->getActiveSheet()->getStyle(...)
—— 样式应最后统一设置,或用
setStyleArray
批量应用 CSV 导入时数值被自动转成科学计数法(如 123456789012 → 1.23E+11) 这不是 PHP 的错,是 Excel/Numbers 打开 CSV 时自动格式化导致的显示问题。原始 CSV 文件里存的仍是纯数字字符串,但用户双击打开就「被转换」了。 PHP 8.5.5 PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。 下载 实操建议: 立即学习 “ PHP免费学习笔记(深入) ”; 导出 CSV 时对数字字段加英文双引号 + 前缀单引号:
"'123456789012"
,Excel 解析时会保留为文本 更稳妥的做法是统一字段类型:导出前把所有可能超长的字段(手机号、订单号、身份证号)强制转为字符串:
(string)$value
,再用
fputcsv($fp, $row, ',', '"')
如果必须支持 Excel 直接打开且保持数值可计算,改用 XLSX 格式导出,并为对应列设置单元格格式:
$sheet->getStyle("A2:A1000")->getNumberFormat()->setFormatCode(\PhpOffice\PhpSpreadsheet\Style\NumberFormat::FORMAT_TEXT);
导入过程中用户刷新页面导致任务中断,如何支持断点续传 PHP 默认没有跨请求状态保持机制,靠 session 或数据库记录进度即可实现,难点在于「如何定义断点粒度」和「避免重复写入」。 实操建议: 立即学习 “ PHP免费学习笔记(深入) ”; 不要以「行号」为断点,而以「唯一业务键」(如订单号、手机号)为准 —— 因为 CSV 行序可能变动,但业务主键不变 导入前先查数据库已有主键集合:
$exists = array_column($pdo->query("SELECT mobile FROM user WHERE mobile IN (".str_repeat('?,', count($batch)-1).'?)')->fetchAll(), 'mobile');
,再过滤掉已存在记录 每次处理完一个批次(如 500 行),把当前最大主键或时间戳写入 Redis:
$redis->set("import:task_{$taskId}:last_key", $lastMobile);
,下次从该位置 SELECT ... WHERE mobile > ? 开始 别依赖
ignore_user_abort(true)
—— 它只防客户端断开,不防 PHP 超时;务必配合
set_time_limit(0)
和 Nginx 的
fastcgi_read_timeout
实际中最容易被忽略的,是 Excel 文件的「共享字符串表」(Shared Strings)机制:PhpSpreadsheet 加载 .xlsx 时,默认把所有文本存进内存数组,哪怕你只读一列,它也全加载。这时候
setReadDataOnly(true)
必须配
setLoadSheetsOnly(['Sheet1'])
,否则照样 OOM。

相关文章