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