PHP安全解压用户上传ZIP需校验文件名防路径遍历,创建临时目录并限制扩展名;读取CSV/Excel前统一转UTF-8、清理BOM;导入前校验字段、去重、分批插入;用PDO事务+错误报告确保数据准确。

PHP 怎么安全解压用户上传的 zip 压缩包
不能直接用 exec('unzip') 或盲目信任 ZipArchive::extractTo() 的目标路径,否则容易被路径遍历攻击(比如压缩包里含 ../../../etc/passwd)。必须校验每个文件名是否合法、是否在预期目录内。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
ZipArchive打开后,遍历所有文件,对每个$zip->getNameIndex($i)调用basename()+realpath()双重校验,确保不含..且不越出解压根目录 - 解压前先创建唯一临时目录(如
sys_get_temp_dir() . '/import_' . uniqid()),并设好权限(0700) - 跳过以
.开头的文件(如.DS_Store)和非.csv/.xlsx文件(按业务限定)
怎么识别并读取解压后的通讯录文件(CSV / Excel)
班级通信录常见格式是 CSV 或 Excel(.xlsx),PHP 默认不支持读取 Excel,需依赖第三方库;而 CSV 可用原生 fgetcsv(),但要注意编码(常见 GBK/UTF-8 混用)。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 检测文件扩展名后,用
mb_detect_encoding(file_get_contents($file), ['UTF-8', 'GBK', 'BIG5'], true)初步判断编码,再用mb_convert_encoding()统一转为 UTF-8 - 读 CSV 时,用
fopen($file, 'r')+fgetcsv(),并设置ini_set('auto_detect_line_endings', 1)兼容 Windows/Mac 换行 - 读 Excel 推荐
phpspreadsheet:用IOFactory::load($file),再取$sheet->toArray();注意限制最大行数(如$reader->setReadDataOnly(true)防内存溢出)
导入前怎么校验数据格式和关键字段
直接入库会因空值、重复学号、非法手机号导致失败或脏数据。校验必须在解压+读取之后、INSERT 之前完成。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 定义必填字段白名单,如
['student_id', 'name', 'class', 'phone'],检查每行是否缺失;用filter_var($phone, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/^1[3-9]\d{9}$/']])校验手机号 - 去重逻辑放在 PHP 层:用
array_unique($data, SORT_REGULAR)不够,应按student_id做键去重($unique[$row['student_id']] = $row) - 批量插入前,用
array_chunk($cleaned_data, 100)分批,避免单次 SQL 过长或超时
怎么把校验后的数据写入 MySQL 并反馈结果
不能用一条 INSERT ... VALUES (...), (...) 硬塞全部数据——失败时无法定位哪一行错;也不能每行都执行一次 INSERT——太慢且易锁表。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用 PDO 预处理语句 +
beginTransaction()包裹整个导入过程,出错则rollback();成功后记录日志(如 “导入 42 行,跳过 3 行重复学号”) - 构建错误报告数组:对每一行,存
['row' => 5, 'error' => '手机号格式错误'],最后 JSON 返回给前端展示 - 特别注意:如果表有唯一索引(如
student_id),用INSERT ... ON DUPLICATE KEY UPDATE替代先查后插,但得明确冲突时是忽略还是覆盖
真正麻烦的不是解压或读取,而是用户上传的文件里藏着 BOM 头、混合编码、Excel 公式残留、隐藏列——这些不会报错,但会导致字段错位或空值入库。每次导入前,强制做 trim() + str_replace("\x{FEFF}", '', $str) 清理 BOM,比事后 debug 快得多。











