我无法在spout php中读取大型xlsx

时间:2017-08-31 10:11:07

标签: php

我有一个大约24 MB大小的xlsx文件。即使我只读第一行也需要太多时间。如果spout逐一读取每一行,那么为什么只要我只读第一行就需要花费太多时间?

以下是完整的代码

require_once 'src/Spout/Autoloader/autoload.php'; 
$file_path = $_SERVER["DOCUMENT_ROOT"].'spout'.'/'.'testdata.xlsx';
use Box\Spout\Reader\ReaderFactory;
use Box\Spout\Common\Type;
libxml_disable_entity_loader(false);

try {
    //Lokasi file excel       
    $reader = ReaderFactory::create(Type::XLSX); //set Type file xlsx
    $reader->open($file_path); //open the file          

    $i = 0; 

    /**                  
    * Sheets Iterator. Kali aja multiple sheets                  
    **/           
    foreach ($reader->getSheetIterator() as $sheet) {   
        //Rows iterator                
        foreach ($sheet->getRowIterator() as $row) {
            echo $i."<hr>";
            if($i==0) // if first row
            {
                print_r($row); 
                exit; // exist after reading first row
            }
            ++$i;
        }       
        exit;
    }   
    echo "Total Rows : " . $i;              
    $reader->close();
    echo "Peak memory:", (memory_get_peak_usage(true) / 1024 / 1024), " MB";
}
catch (Exception $e) {
    echo $e->getMessage();
    exit;   
}

你能帮我解决一下这个问题的原因吗?我怎么能快速做到? 您可以在http://www.mediafire.com/file/y369njsaeeah1ip/testdata.xlsx

找到测试xlsx文件

Excel文件包含以下内容:

  • 行数:999991
  • 列数:4(即MPN,CATEGORY,MFG,描述)
  • 文件大小约为24 MB,不包含任何格式。 enter image description here

1 个答案:

答案 0 :(得分:0)

有两种方法可以使用XLSX文件存储单元格数据:

  1. 最简单的一个是保持单元格值与单元格结构(即单元格&#34; A1&#34;包含&#34; foo&#34;,&#34; B1&#34;包含&#34;酒吧&#34;。)
  2. 另一种方法是跟踪电子表格中使用的不同值,并添加一个有助于删除重复项的重定向层:这转换为2个文件,一个描述结构(即单元格&#34; A1&#34;包含由ID1引用的值,&#34; B1&#34; =&gt; ID2,&#34; C1&#34; =&gt; ID1)和描述值的一个(ID1 =&gt;&#34; foo&#34;,ID2 =&gt;&#34; bar&#34;)。
  3. 方法2优化文件的大小,因为使用N次的值将仅存储一次(但是被引用N次)。但是,要读取这些值,您现在需要读取2个文件,并在读取结构时准备好映射。基本上,为了读取第一行,您将读取结构以获取单元格(A1,B1,C1),然后您需要使用ID来解析值。

    内联方法更直接,因为所有内容都存储在同一个地方,因此您可以同时读取结构和值。不需要映射表。

    现在回到你的问题!您尝试阅读的文件最有可能使用方法2(描述电子表格结构的文件+包含所有值的文件)。当Spout进入阅读器时,它会处理包含值的文件,以便每当我们开始准备好行时映射就会就绪。

    如果有很多值,此处理可能需要很长时间。低于某个阈值(取决于可用的内存量),Spout加载映射[ID =&gt;值]进入内存,这是非常快的。但是,如果值太多,Spout会确定所有内容都不适合内存并缓存磁盘上映射的块。这个过程绝对是耗时的......

    所以这就是你的情况。希望现在更有意义。 最终门槛将会提高,因为Spout目前处于超级防御状态以避免内存不足问题。