php后台处理excel上传需先校验$_files['file']的类型、大小及临时文件有效性,再用phpspreadsheet加载并清洗数据,逐行解析、转换日期、过滤空行,预检主键与字段合法性,分批插入并返回结构化错误详情。

PHP后台怎么处理Excel文件上传
直接用 $_FILES 接收上传的 Excel 文件,但别急着读内容——先校验类型和大小。很多线上环境默认禁用 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet 这类 MIME 类型,光靠前端 accept=".xlsx,.xls" 没用,后端必须检查 $_FILES['file']['type'] 和 $_FILES['file']['tmp_name'] 是否真实存在且可读。
常见错误:本地测试能跑,上线报 Failed to open stream: No such file or directory,大概率是临时目录权限不对或 upload_tmp_dir 被禁用。建议加一行 is_uploaded_file($_FILES['file']['tmp_name']) 做兜底判断。
- 优先用
.xlsx格式,.xls(BIFF8)兼容性差,部分 PHP 扩展已不维护 - 上传限制要同步改
php.ini:确保upload_max_filesize和post_max_size都大于预期文件体积 - 别把
$_FILES['file']['name']直接拼进路径,防止路径遍历,用basename()+ 随机前缀重命名
用 PhpSpreadsheet 读取数据并过滤空行
Composer 安装 phpoffice/phpspreadsheet 后,核心逻辑就三步:加载、选表、逐行取值。但实际导入时,Excel 里常有合并单元格、空标题行、备注行,直接 $worksheet->toArray() 会把空行、格式行全塞进来。
推荐做法是先定位有效数据起始行(比如跳过前 3 行),再用 $worksheet->getHighestRow() 和 $worksheet->getHighestColumn() 界定范围,手动循环读取:
$highestRow = $worksheet->getHighestRow();
$highestColumn = $worksheet->getHighestColumn();
for ($row = 4; $row getCell($highestColumn . $row)->getValue();
if (empty($cellValue) && $row > 4) break; // 遇到首列为空就停
$data[] = $worksheet->rangeToArray('A' . $row . ':' . $highestColumn . $row)[0];
}
-
toArray()默认包含空单元格,设第三个参数为false可跳过空值:$worksheet->toArray(null, true, true, false) - 日期字段会变成 Excel 序列号(如 44562),需用
PhpOffice\PhpSpreadsheet\Shared\Date::excelToDateTimeObject()转换 - 如果第一行是标题,记得
array_shift($data)并用它生成字段映射,避免硬编码下标
批量插入前怎么防重复和类型错乱
用户上传的 Excel 经常混着空格、全角数字、隐藏字符,直接 INSERT INTO 很容易因唯一键冲突或类型转换失败中断整个批次。不能依赖数据库报错来控制流程。
必须在 PHP 层做两件事:清洗字段 + 预检主键/唯一索引是否存在。比如手机号列,先 trim()、str_replace(' ', '', $val)、再用 filter_var($val, FILTER_VALIDATE_INT) 或正则粗筛;对邮箱列用 filter_var($val, FILTER_VALIDATE_EMAIL)。
- 查重不要等插完再查,用
INSERT ... ON DUPLICATE KEY UPDATE或先SELECT id FROM table WHERE code IN (...)批量预查 - 整数字段若含空字符串,
(int) ''会转成 0,导致脏数据,应显式判断if (empty($val)) { $val = null; } - 单次插入别超 500 行,大文件分批处理,避免超时,用
set_time_limit(0)仅是掩耳盗铃,Nginx/Apache 的超时配置更关键
导入结果怎么返回给前端才不丢信息
别只返回“成功 123 条”,用户需要知道哪几行失败、为什么失败。后端得记录每行的校验结果,组装成结构化数组返回 JSON。
典型返回结构示例:
{
"total": 150,
"success": 142,
"failed": [
{"row": 7, "reason": "手机号格式错误"},
{"row": 45, "reason": "邮箱已存在"},
{"row": 88, "reason": "必填字段为空"}
]
}
- 失败原因必须具体到单元格位置(如“第 7 行 D 列”),方便用户定位修改
- 不要在循环里每行都查一次数据库,把所有待查的手机号/邮箱先 collect 成数组,一次
IN查询搞定 - 如果用了事务,出错时记得
$pdo->rollback(),但别把整个事务撑到 1000 行——分段 commit 更稳妥
最易被忽略的是字符编码:Excel 文件本身可能带 BOM,读出来中文变乱码;MySQL 连接没设 utf8mb4,插入时报错或截断。从上传、读取、连接、建表,编码链路上任何一环断掉,都会让导入在某个环节静默失败。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











