phpspreadsheet导出Excel报错问题汇总

文章目录

phpspreadsheet导出Excel报错问题汇总

使用 phpspreadsheet 导出Excel的一段代码如下:

php 复制代码
$sheet->setCellValue($cellName[$kk] . ($key + 2), $vv);

换行符(\n)问题

如果某个字段值最后面包含 换行符(\n) 会导致报错:

复制代码
Invalid numeric value for datatype Numeric

解决办法:

  • 如果需要保留最后面的换行符,可以追加一个空白字符:
php 复制代码
if (substr($vv, -1) == "\n" || substr($vv, -1) == "\t") {
     $vv .= " ";
}
$sheet->setCellValue($cellName[$kk] . ($key + 2), $vv);
  • 如果不需要保留最后面的换行符,使用trim过滤即可:
php 复制代码
$vv=trim($vv);
$sheet->setCellValue($cellName[$kk] . ($key + 2), $vv);

等号(=)问题

如果某个字段值最后面包含 等号(=) 会导致报错:

复制代码
Worksheet!xxx -> Formula Error: An unexpected error occurred

原因分析:在 excel 中,如果某个单元格中的值是以 = 开头,则表示这个单元格是根据其他单元格的值计算出来的,= 后面需要跟着一个合法的表达式。但是此处我并不是要计算一个表达式,而是因为字段值以=开头了。那么,解决方案就是在 = 前面加个其他字符。代码如下:

php 复制代码
if (strpos($vv, '=') === 0 ) {
    $vv = "'" . $vv; // 在 = 前面加个单引号
}

完整方法示例

使用phpspreadsheet 导出Excel的方法示例如下:

安装扩展:composer require phpoffice/phpspreadsheet

php 复制代码
<?php

namespace app\common\utils;

use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Exception;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;

class FileUtil
{
	/**
     * 导出Excel文件
     * @param array $list 数据列表数组
     * @param array $field_map 数组字段数组
     * @param string $file_path 文件路径
     * @param string $filename 文件名称
     * @param bool $filename_append_now_time 文件名是否追加当前时间
     * @param string $suffix 文件后缀
     * @return false|string
     * @throws Exception
     */
    public static function outputExcel(array $list, array $field_map, string $file_path, string $filename, bool $filename_append_now_time = true, string $suffix = 'xlsx')
    {
        //处理 $list 数据列表
        $data = [];
        foreach ($list ?? [] as $v) {
            $item = [];
            foreach ($field_map as $field_key => $field_val) {
                //解析 $field_map 中 数组多级key的数据,需要在 $field_map 中定义多个key 以.分隔, 例如: user.info.name
                $field_key_split = explode('.', $field_key);
                $container = $v;
                for ($index = 0; $index <= 2; $index++) {
                    if (isset($field_key_split[$index]) && isset($container[$field_key_split[$index]])) {
                        $container = $container[$field_key_split[$index]];
                    }
                }
                if (is_array($container)) {
                    $container = '';
                }
                $item[$field_key] = $container;
            }
            //$data[] = array_values($item);
            $data[] = $item;
        }

        //表头A-Z列定义
        $cellName = [];
        for ($i = 'A'; $i <= 'Z'; $i++) {
            $cellName[] = $i;
        }
        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();

        //设置表头行
        $titleList = array_values($field_map);
        //设置表头单元格样式
        $styleArray = [
            'font' => [
                'bold' => true, // 字体加粗
                'color' => [
                    'rgb' => '0000ff',
                ],
            ]
        ];
        foreach ($titleList as $tkey => $tval) {
            $sheet->setCellValue($cellName[$tkey] . '1', $tval);
            $sheet->getStyle($cellName[$tkey] . '1')->applyFromArray($styleArray);
        }

        //设置表数据内容行和列
        foreach ($data ?? [] as $key => $val) {
            $val = array_values($val);
            foreach ($val as $kk => $vv) {
                if (is_string($vv)) { //对于字符串类型,防止较长的数字字符串被格式化为科学计数法
                    $sheet->setCellValueExplicitByColumnAndRow(($kk + 1), ($key + 2), $vv, DataType::TYPE_STRING);
                } else {
                    //导出的某个字段值里面包含换行符(\n)会导致报错,因此需要过滤前后的空格和换行符
                	$vv=trim($vv);
                	//导出的数据中如果存在 "=" 开头的字符串,会出错 Formula Error: An unexpected error occurred",因此需要对 "=" 特殊处理
                    if (strpos($vv, '=') === 0 ) {
                        $vv = "'" . $vv; // 在 = 前面加个单引号
                    }
                    $sheet->setCellValue($cellName[$kk] . ($key + 2), $vv);
                }
            }
        }

        //创建目录并写入Excel文件
        if (!is_dir($file_path)) {
            if (!mkdir($file_path, 0777, true)) {
                return false;
            }
        }
        if ($filename_append_now_time) {
            $filename .= '-' . date("YmdHis", time());
        }
        $filename .= '.' . strtolower($suffix);
        $objWriter = new Xlsx($spreadsheet);
        $objWriter->save($file_path . '/' . $filename);

        return $filename;
    }

}

调用部分示例:

php 复制代码
$list = json_decode('[{"id":1,"username":"jihongchu","mobile":"177888","nickname":"王先生","avatar":"https://profile-avatar.csdnimg.cn/default.jpg","status":1,"time":{"last_login_time":"2024-03-22 14:42:14","add_time":"2024-03-22 14:42:14","update_time":"2024-03-27 10:47:57"}}]',true);

//设置导出的字段和表头
$field_map = [
    "id" => "ID",
    "username" => "用户名",
    "mobile" => "手机号",
    "nickname" => "昵称",
    "status" => "状态",
    //针对二维模型导出的字段定义
    "time.last_login_time" => "最后登录时间",
    "time.add_time" => "添加时间",
    "time.update_time" => "修改时间",
];
$file_path = '/Users/xxx/Downloads'; //绝对路径
$file_title = '用户数据导出-' . time();
$output_file_name = FileUtil::outputExcel($list, $field_map, $file_path, $file_title);

导出结果:

相关推荐
JaguarJack7 小时前
FrankenPHP 原生支持 Windows 了
后端·php·服务端
BingoGo8 小时前
FrankenPHP 原生支持 Windows 了
后端·php
JaguarJack1 天前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo1 天前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack2 天前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay3 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954483 天前
CTF 伪协议
php
BingoGo5 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack5 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo6 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php