在 PhpSpreadsheet 中读取 Xlsx 文件

Posted

技术标签:

【中文标题】在 PhpSpreadsheet 中读取 Xlsx 文件【英文标题】:Read Xlsx file in PhpSpreadsheet 【发布时间】:2019-05-18 07:01:36 【问题描述】:

我想读取在 Microsoft Excel 中创建的xlsx 文件,但是当我运行以下代码时...

$Source_File = "test.xlsx";
$Spreadsheet = \phpOffice\PhpSpreadsheet\IOFactory::load($Source_File);

...我收到以下错误:

Fatal error: Uncaught PhpOffice\PhpSpreadsheet\Reader\Exception: Unable to identify a reader for this file in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php:163
Stack trace:
  #0 /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php(93): PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile('file:///home/ar...')
  #1 /var/www/html/Function_Spreadsheet.php(480): PhpOffice\PhpSpreadsheet\IOFactory::load('file:///home/ar...')
  #2 /var/www/html/Function_Home.php(3747): Spreadsheet_Reader_1('/var/www/html/F...', 3745, Array, Array)
  #3 main thrown in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php on line 163

如果我改用$Spreadsheet = IOFactory::load($Source_File);,我会得到同样的错误

如果我改用$Spreadsheet = $reader->load($Source_File);,则会收到以下错误

Warning: ZipArchive::getFromName(): Invalid or uninitialized Zip object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 311

Warning: ZipArchive::getFromName(): Invalid or uninitialized Zip object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 313

Notice: Trying to get property 'Relationship' of non-object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 350

Warning: Invalid argument supplied for foreach() in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 350

Warning: ZipArchive::getFromName(): Invalid or uninitialized Zip object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 311

Warning: ZipArchive::getFromName(): Invalid or uninitialized Zip object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 313

Notice: Trying to get property 'Relationship' of non-object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 397

Warning: Invalid argument supplied for foreach() in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 397

Warning: ZipArchive::getFromName(): Invalid or uninitialized Zip object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 311

Warning: ZipArchive::getFromName(): Invalid or uninitialized Zip object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 313

Notice: Trying to get property 'Override' of non-object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 1855

Warning: Invalid argument supplied for foreach() in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 1855

Warning: ZipArchive::close(): Invalid or uninitialized Zip object in /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php on line 1883

在 Ubuntu 18.04 上的 Apache 中,我的 PHP v7.2 脚本可以读取和打开该文件。我阅读了几篇论坛帖子,其中提出了以下建议:

Install the required extensions 并重启 apache Check the file permissions

我尝试在 LibreOffice 中打开文件并将其另存为 xlsx,但出现同样的错误(如果我另存为 xls,则不会出现错误)。

我可以创建一个阅读器 $reader = new \PhpOffice\PhpSpreadsheet\Reader\Xlsx();,但是当我创建 $Spreadsheet = $reader->load($Source_File);$Spreadsheet = IOFactory::load($Source_File); 时,我得到了同样的错误。

另外,我可以创建一个可以读取 xls 文件的 xls 阅读器。我也可以创建一个 xlsx 阅读器,但它不会读取 xlsx 文件,尝试读取 xlsx 文件时会出现相同的错误。 那么,为什么xlsx 文件会出现错误?

另外,我阅读了错误消息指向的源代码(IOFactory.php),并找到了发生错误的以下位置(靠近第 #139 行)...

//Let's see if we are lucky
if (isset($reader) && $reader->canRead($filename))

    return $reader;

...我搜索了canRead 的定义,但在/vendor/phpoffice/phpspreadsheet/ 的任何地方都没有找到。 canRead 是在哪里定义的? 我想如果我能读懂canRead 的定义,那么也许我会明白问题的根本原因是什么。

更新:

我从 cmets 和讨论中了解到 canRead() 是在 \PhpSpreadsheet\Reader\Xlsx.php 中定义的,从第 65 行开始。在 canRead() 中,$zip->open($pFilename) 返回错误代码 ZipArchive::ER_NOENT,意思是“No such file”。但是,该文件存在。 那么,为什么会出现这个错误

更新 - 2018-12-18

This web page 表示有多种类型的 xlsx 文件。所以,我跑了file test.xlsx,它显示了Microsoft Excel 2007+。然后我在 LibreOffice Calc 中打开电子表格并将其保存为 OOXML 类型的 xlsx 文件并重新运行 file test.xlsx,显示 Microsoft OOXML。然后我重新运行了 PHP 脚本,但得到了同样的错误。所以,我的xlsx 文件类型似乎不是问题。

所以,我决定使用PHPExcel(尽管它已被弃用)来完成一些必要的工作。当我使用 PHPExcel 运行脚本时,我收到了关于 canRead() 无法检测到 xlsx 文件的类似错误。

所以,我继续阅读this web page 并遵循wesood 的最后一个建议,该建议来自this web page 上已接受的答案。这个解决方案对我有用:在文件/PHPExcel/IOFactory.php 中,我在if (isset($reader) && $reader->canRead($filename)) 之前添加了PHPExcel_Settings::setZipClass(\PHPExcel_Settings::PCLZIP);

但是,我仍然想知道如何在 PhpSpreadsheet 中解决这个问题。看来我需要更多地了解pclzip 的工作原理以及是否需要使用 PhpSpreadsheet 完成类似的操作。

2019 年 2 月 10 日更新:

我今天尝试运行脚本,似乎添加 PHPExcel_Settings::setZipClass(\PHPExcel_Settings::PCLZIP); 不再有效。所以,我又卡住了……

我做错了什么?欢迎任何帮助!

2019 年 2 月 18 日更新:

根据 cmets 的建议,我使用通过 Google 搜索结果(例如,this file)找到的随机 XLSX 文件测试了脚本,这些文件是 Excel 2007+Microsoft OOXML 类型,对于 PhpSpreadsheet 显示相同的错误:

致命错误:未捕获的 PhpOffice\PhpSpreadsheet\Reader\Exception: 无法识别此文件的阅读器 /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php:176 堆栈跟踪:#0 /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php(113): PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile('file:///var/www...') #1 /var/www/html/Function_Spreadsheet.php(798): PhpOffice\PhpSpreadsheet\IOFactory::identify('file :///var/www...') #2 /var/www/html/Function_Home.php(3748): Spreadsheet_Reader_1('/var/www/html/F...', 3746, 数组, 数组) #3 main 在第 176 行的 /var/www/html/vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/IOFactory.php 中抛出

【问题讨论】:

你要找的代码是:github.com/PHPOffice/PhpSpreadsheet/blob/develop/src/… 我只能假设,根据您所说的以及查看 GitHub 代码,您尝试加载的文件不是 phpsreadsheet 认为应该是的 xlsx 格式 你能用 7-zip 打开你的测试文件Test.xlsx 来验证它是一个 ZIP 文件吗?您正在存档中查找名为 workbook.xml 的文件 它不能给出相同的错误 - 它不使用相同的代码。 load 函数位于我之前引用的同一文件的第 389 行。您能再检查一下load 函数给出的错误吗? 每当您提出问题时,请在问题中添加主标签。人们通常在看主要标签。我添加了 php 标签。如果您早点添加它,我会早点看到它。您的问题没有所需的可见性的原因之一是缺少 php 标签。 【参考方案1】:

据我了解,您缺少一块。为什么不先创建一个阅读器,然后加载文件。

试试下面的代码。 它可以识别扩展并相应地创建该类型的阅读器。

$inputFileName = "Text.xlsx";

/**  Identify the type of $inputFileName  **/
$inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify($inputFileName);

/**  Create a new Reader of the type that has been identified  **/
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);

/**  Load $inputFileName to a Spreadsheet Object  **/
$spreadsheet = $reader->load($inputFileName);

/**  Convert Spreadsheet Object to an Array for ease of use  **/
$schdeules = $spreadsheet->getActiveSheet()->toArray();

现在您可以简单地在结果数组上运行 foreach 循环。

foreach( $schdeules as $single_schedule )
               
    echo '<div class="row">';
    foreach( $single_schedule as $single_item )
    
        echo '<p class="item">' . $single_item . '</p>';
    
    echo '</div>';

这是经过测试和工作的代码。

【讨论】:

我收到$inputFileType = \PhpOffice\PhpSpreadsheet\IOFactory::identify($inputFileName); 的以下错误:Fatal error: Uncaught PhpOffice\PhpSpreadsheet\Reader\Exception: Unable to identify a reader for this file 您对为什么会发生此错误有任何想法吗?我使用的 XLSX 输入文件是 Microsoft Excel 2007+ 文件类型。 那么您的文件中可能有一些错误。您为什么不尝试使用示例文件?这是标准样本的github urlgithub.com/PHPOffice/PhpSpreadsheet/tree/master/samples/Reader/… 如果它适用于示例,那么您可以检查您的文件有什么问题以及您的文件和工作文件之间的区别。 您提供的 github 链接中的示例都不是 XLSX 文件,我只看到 CSV、TSV 和 XLS 文件。是否有我没有注意到的 XLSX 文件? 我下载了另外几个 XLSX 文件 [file-examples.com/wp-content/uploads/2017/02/…,它们要么是 Excel 2007+ 要么是 Microsoft OOXML,但仍然显示相同的错误。所以,看来问题不在于 XLSX 文件。也许这是我系统上的一个配置,但它可能是什么?【参考方案2】:

在我的 Mac 上将 .xlsx 文件添加到 git 存储库后,我遇到了同样的问题。 问题是 git 自动转换了行尾。

解决方案是将这些行添加到.gitattributes 文件中:

*.xls   binary
*.xlsx  binary

【讨论】:

【参考方案3】:

我在尝试加载 XLSX 文件时遇到了同样的错误。就我个人而言,我发现了一个非常简单的解决方法来解决我的问题。我手动从文件名中获取扩展名xlsx。我注意到我的一些其他代码使用旧的 PHP 电子表格库正在扩展 Xls。所以我尝试在Xlsx 中加载,效果很好。

这是我用来正确加载扩展的代码。它只是抓取最后一个句点之后的所有字符,然后捕获该子字符串的第一个字符。 ucfirst 只是将传递给它的字符串的第一个字母大写。 substr 返回一个子字符串,其中第一个参数是要从中获取的字符串,第二个参数是给定字符串中子字符串的起始索引。最后strrpos 找到给定字符串中最后一次出现的子字符串。

https://www.php.net/manual/en/function.ucfirst.php

https://www.php.net/manual/en/function.strrpos

https://www.php.net/manual/en/function.substr.php

$inputFileType = ucfirst(substr($cccFile, strrpos($cccFile, '.') + 1));

/**  Create a new Reader of the type defined in $inputFileType  **/
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReader($inputFileType);

有一次,我添加了ucfirst 命令,它为我解决了这个问题。

【讨论】:

请解释第一行的作用。 我在第二段解释得很清楚。 我了解代码,但不了解代码中strrpossubstr + 1 和/或ucfirst 的人可能会感到困惑。 很公平,我会尝试添加一些澄清文字。【参考方案4】:

使用这个。它将显示 .xlsx

   $inputFileName = public_path('asset/docs/Filename.xlsx');
    
   /** Load $inputFileName to a Spreadsheet Object  **/
   $spreadsheet = \PhpOffice\PhpSpreadsheet\IOFactory::load($inputFileName);
   $writer = IOFactory::createWriter($spreadsheet, 'Html');
   $message = $writer->save('php://output');

【讨论】:

【参考方案5】:

我在使用“private://...”作为文件路径的 Drupal 页面中遇到了类似的问题。 PHPExcel 中的 getFromZipArchive(在 PhpSpreadsheet 中应该类似)无法读取像“private://...”这样的文件路径方案。翻译成本地路径后,就可以了。

在你的情况下,你有像“file:///home/ar...”这样的路径方案。所以请改用“/home/ar...”。

【讨论】:

以上是关于在 PhpSpreadsheet 中读取 Xlsx 文件的主要内容,如果未能解决你的问题,请参考以下文章

PHPSpreadsheet/XLSX:如何在 LibreOffice 中默认隐藏评论

从 csv 文件读取数据并使用 phpspreadsheet 写入 excel

PhpSpreadsheet 块过滤器读取返回空行

使用 PHPSpreadsheet 打开受密码保护的 XLSX 文件

PHPSpreadsheet:如何获取加载的行数?

如何在 PHPSpreadsheet 中更改单元格颜色