ThinkPHP无内置事件驱动导入机制,事件仅适合导入完成后的通知;批量导入必须用Db::insertAll()而非模型循环创建;CSV比Excel更省内存但需处理编码和换行;大文件解析须流式读取并调优内存与超时。

bind、listen 或自定义事件)里执行,本质仍是手动触发或控制器调用。直接在控制器里写逻辑更可控,强行套事件反而容易绕晕、难调试。
ThinkPHP 6 的 event 系统不自动触发导入
你不能指望用户上传 Excel 后,框架“自动监听到文件就跑导入逻辑”。event 是被动触发的,必须由你显式 dispatch,比如:event('ExcelImported', $data)。它不替代上传接收、文件读取、数据校验这些核心动作。
- 事件适合做“导入完成后的通知”,比如发邮件、写日志、清缓存,而不是主流程
- 若把
IOFactory::load()、insertAll()全塞进事件回调里,会导致错误堆栈难追踪、事务难控制、内存溢出风险更高 - ThinkPHP 6 默认事件是同步执行的,没加队列的话,大文件导入会卡住 HTTP 请求
Db::insertAll() 和 Model::create() 别混用
批量导入必须走 insertAll(),别用模型单条 create() 循环——哪怕加了事务,1000 行也极慢。
-
Db::name('user')->insertAll($rows)是原生批量 INSERT,快且可控 -
UserModel::create($row)每次都走验证、事件、自动时间戳等开销,100 行以上明显拖慢 - 如果必须用模型(比如要字段过滤或自动转换),改用
UserModel::insert($rows, false, true),第三个参数true表示跳过验证,第二个false表示不替换主键
CSV 导入比 Excel 更稳,但得自己处理编码和换行
Excel 解析依赖 PhpSpreadsheet,大文件易爆内存;CSV 用 fgetcsv() 流式读,内存友好,但坑多。
- Windows 下 Excel 保存的 CSV 默认是 GBK 编码,
fgetcsv()读出来中文是乱码,得先mb_convert_encoding(file_get_contents($file), 'UTF-8', 'GBK')再写临时文件,或用stream_filter_append($handle, 'convert.iconv.GBK/UTF-8') - CSV 单元格含换行符(如地址字段)时,
fgetcsv()会误判为多行,必须确保导出时用双引号包裹字段(Excel 默认开启) - 不要用
file()一次性读全文件,10MB CSV 就可能 OOM;坚持用fopen() + fgetcsv()迭代
PhpSpreadsheet 加载大 Excel 必须设内存和超时
默认配置下,5MB 以上的 .xlsx 文件大概率报 Allowed memory size exhausted 或 Maximum execution time exceeded。
立即学习“PHP免费学习笔记(深入)”;
- 在导入方法开头加:
ini_set('memory_limit', '512M');和set_time_limit(300); - 避免用
toArray()一次性加载全部数据,改用$worksheet->getRowIterator()配合$row->getCellIterator()流式遍历 - 读完立即
unset($spreadsheet),否则对象残留导致内存不释放
Db::insertAll()。事件只该出现在“导入成功后”这个节点,比如记录操作日志或推送消息,别让它参与主干。



















