优先使用 PhpSpreadsheet 读取 Excel 文件,它支持 .xlsx、.xls、.ods 格式且持续维护;禁用已废弃的 PHPExcel 和错误使用 fgetcsv 解析 .xlsx;需启用 zip 扩展并正确安装依赖。

PHP读取Excel文件,优先用 PhpSpreadsheet
直接结论:别碰 PHPExcel(已废弃),也别用 fgetcsv() 硬解析 .xlsx——它根本不是CSV。当前唯一靠谱的选择是 PhpSpreadsheet,它能正确处理 .xlsx、.xls、.ods,且仍在维护。
常见错误现象:Call to undefined function excel_read_file()(压根没装库)、ZipArchive not available(没开 zip 扩展)、读出来全是空值(误把 .xlsx 当文本流直接 fopen)。
- 安装命令必须带
--with-zip(PHP编译时)或确认extension=zip已启用 -
composer require phpoffice/phpspreadsheet是唯一推荐安装方式,手动下载 ZIP 包容易漏依赖 - 读
.xls(旧版)需额外装phpoffice/phpspreadsheet的compatibility模块,但建议先转成.xlsx测试
读取 .xlsx 文件的最小可行代码
不是“Hello World”式演示,而是能立刻跑通、带错误兜底的真实片段:
use PhpOffice\PhpSpreadsheet\IOFactory;
try {
$spreadsheet = IOFactory::load('/path/to/file.xlsx');
$worksheet = $spreadsheet->getActiveSheet();
$data = $worksheet->toArray(); // 二维数组,从第一行第一列开始
} catch (\PhpOffice\PhpSpreadsheet\Reader\Exception $e) {
// 注意:这个异常类名要写全,漏命名空间会静默失败
error_log('Excel读取失败:' . $e->getMessage());
$data = [];
}
关键点:toArray() 默认跳过空行空列,但不会自动过滤表头;如果首行是字段名,后续处理得自己 array_shift($data)。
立即学习“PHP免费学习笔记(深入)”;
-
IOFactory::load()自动识别格式,不用手动指定Xlsx或Xls - 路径必须是服务器可读的绝对路径,
$_FILES['file']['tmp_name']传进来前要is_uploaded_file()校验 - 大文件(>5MB)会触发内存不足,此时必须用
setReadDataOnly(true)+setLoadSheetsOnly(['Sheet1'])限域加载
中文乱码、日期变数字、公式不计算?不是编码问题
这些不是 mb_convert_encoding() 能解决的——PhpSpreadsheet 本身以 UTF-8 处理所有文本。乱码通常源于 Excel 文件保存时用了非 Unicode 编码(比如 GBK),但更常见的是你读到了“原始单元格值”,而非“显示值”。
- 日期显示为
44205:这是 Excel 的序列号,调用$cell->getFormattedValue()而不是$cell->getValue() - 公式显示为
=SUM(A1:A10)而非结果:默认不计算,需$spreadsheet->getActiveSheet()->getCell('B1')->getCalculatedValue() - 从 Windows 机器导出的 Excel 含 BOM 或隐藏字符:用
trim()+mb_trim(<code>$str, 'utf-8') 清洗,别信“改 header 字符集”
为什么不用 simplexlsx 或 box/spout?
simplexlsx 轻量但只支持 .xlsx,且对合并单元格、样式、密码保护完全无感;spout 内存友好,但只能读不能写,且不支持 .xls 和公式计算。
选型依据很实际:如果你要读用户上传的 Excel(含格式/合并/公式/多 sheet),PhpSpreadsheet 是目前唯一不妥协的选择。它的慢和吃内存是真实代价,但换来的稳定性值得——尤其当业务要求“和 Excel 打开看到的一样”。
真正容易被忽略的点:PhpSpreadsheet 的 toArray() 方法在有合并单元格时,会把值重复填充到所有合并区域单元格里,而不是留空。如果你依赖“空单元格判断逻辑”,这里会悄悄出错。











