使用PHP从企业应用程序生成MySQL数据的大型Excel文件

我们正在开发和维护一些系统,这些系统需要以Excel格式将报告导出到最终用户。 这些报告是从一个MySQL数据库中收集的,并且通常会产生大约40000行10-15列的数据,我们期望数据量稳步增长。

目前我们正在使用PHPExcel进行Excel生成,但是它不再适用于我们。 在超过5000行之后,内存消耗和加载时间变得无法忍受,无法通过无限增加PHP对内存使用和脚本执行时间的最大限制来解决。 数据处理尽可能精简,整个问题是PHPExcel是一个内存pipe理器。 CSV的生成会更轻,但不幸的是我们需要从我们的服务中导出Excel(和Excel单独),由于用户的需求。 这是由于格式化要求等,所以CSV不是一个选项。

对第三方应用程序/模块/服务的任何想法/build议/什么曾经为生成大型卓越? 如果它是一个商业许可证并不重要,只要它符合我们的需求,就可以集成到现有的PHP应用程序中并完成其工作。 我们的服务通常在linux / php / mysql上运行,我们可以做任何我们需要做的服务器。

谢谢!

对于如此大量的数据,我不会推荐像PHPExcel或ApachePOI(用于Java)的工具,因为它们的内存要求。 我最近在类似的任务中挣扎,我发现了方便的方式(但也许有点费劲)将数据注入电子表格。 服务器生成或更新Excel电子表格可以实现简单的XML编辑。 我有坐在服务器上的XLSX电子表格,每次从dB收集数据,我使用php解压缩。 然后,我访问特定的XML文件,这些文件包含需要注入的工作表的内容并手动插入数据。 之后,我压缩电子表格文件夹以便将其作为常规XLSX文件分发。 整个过程相当快速和可靠。 很明显,与XLSX / Open XML文件的内部组织有关的问题和故障(例如,Excel倾向于将所有string存储在单独的表中,并在工作表中使用对该表的引用)。 但是当仅注入数字和string等数据时,并不那么困难。 如果有人有兴趣,我可以提供一些代码。

好的,这里是示例代码。 我试图评论它做了什么,但可以自由地要求进一步的解释。

<?php /** * Class for serverside spreadsheet data injecting * Reqs: unzip.php, zip.php (containing any utility functions able to unzip files & zip folders) * * Author: Poborak */ class DataInjector { //spreadsheet file, we inject data into this one const SPREADSHEET_FILE="datafile.xlsx"; // specific worksheet into which data are being injected const SPREADSHEET_WORKSHEET_FILE="/xl/worksheets/sheet7.xml"; //working directory, spreadsheet is extracted here const WSPACE_DIR="Wspace"; // query for obtaining data from DB const STORE_QUERY = "SELECT * FROM stores ORDER BY store_number ASC"; private $dbConn; private $storesData; /** * @param mysqli $dbConn */ function __construct(mysqli $dbConn) { $this->dbConn = $dbConn; } /** * Main method for whole injection process * First data are gathered from DB and spreadsheet is decompressed to workspace. * Then injection takes place and spreadsheet is ready to be rebuilt again by zipping. * * @return boolean Informace o úspěchu */ public function injectData() { if (!$this->getStoresInfoFromDB()) return false; if (!$this->explodeSpreadsheet(self::SPREADSHEET_FILE,self::WSPACE_DIR)) return false; if (!$this->injectDataToSpreadsheet(self::WSPACE_SUBDIR.self::SPREADSHEET_WORKSHEET_FILE)) return false; if (!$this->implodeSpreadsheet(self::SPREADSHEET_FILE,self::WSPACE_DIR)) return false; return true; } /** * Decompress spreadsheet file to folder * * @param string $spreadsheet * @param string $targetFolder * * @return boolean success/fail */ private function explodeSpreadsheet($spreadsheet, $targetFolder) { return unzip($spreadsheet,$targetFolder); } /** * Compress source folder to spreadsheet file * * @param string $spreadsheet * @param string $sourceFolder * * @return boolean success/fail */ private function implodeSpreadsheet($spreadsheet, $sourceFolder) { return zip($sourceFolder,$spreadsheet); } /** * Loads data from DB to member variable $storesDetails (as array) * * @return boolean success/fail */ private function getStoresInfoFromDb() { unset($this->storesData); if ($stmt = $this->dbConn->prepare(self::STORE_QUERY)) { $stmt->execute(); $stmt->bind_result($store_number, $store_regional_manager, $store_manager, $store_city, $store_address); while ($stmt->fetch()) { $this->storesData[trim($store_number)] = array(trim($store_regional_manager),trim($store_manager),trim($store_address),trim($store_city)); } $stmt->close(); } return true; } /** * Injects data from member variable $storesDetails to spreadsheet $ws * * @param string $ws target worksheet * * @return boolean success/fail */ private function injectDataToSpreadsheet($ws) { $worksheet = file_get_contents($ws); if ($worksheet === false or empty($this->storesData) return false; $xml = simplexml_load_string($worksheet); if (!$xml) return false; // Loop through $storesDetails array containing rows of data foreach ($this->storesData as $std){ // For each row of data create new row in excel worksheet $newRow = $xml->sheetData->addChild('row'); // Loop through columns values in rowdata foreach ($std as $cbd){ // Save each column value into next column in worksheets row foreach ($this->storesData as $cbd){ $newCell = $newRow->addChild('c'); $newCell->addAttribute('t', "inlineStr"); $newIs = $newCell->addChild('is'); // text has to be saved as utf-8 (otherwise the spreadsheet file become corrupted) if (!mb_check_encoding($cbd, 'utf-8')) $cbd = iconv("cp1250","utf-8",$cbd); $newT = $newIs->addChild('t',$cbd); } } } // Save xml data back to worksheet file if (file_put_contents($ws, $xml->asXML()) !== false) return true; } } ?> 

我试图保持最新的PHPExcel的替代品列表在这里

如果你的速度/内存性能超过了PHPExcel所能提供的,那么我真正推荐的是Ilia的 libXL 封装扩展 ,因为这个库仍然被支持。

检查OfficeWriter 。 我们最近专门为财富500强财务公司提高了海量数据集的性能。 它的文件格式比你特别需要的还要多(图表和你有什么),但是这个API非常易于使用,通过评估你可以快速得到一个POC。 免责声明 – 我在build立最新版本的工程师。

另外一个缺点就是.NET。

那么打印表呢?

 <?php header("Content-Type: application/vnd.ms-excel; charset=utf-8"); header("Content-Disposition: attachment; filename=abc.xls"); //File name extension was wrong header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: private",false); echo "<table><tr><td>Test</td><td>Test2</td></table>"; 

你可以导出为CSV格式,Excel可以处理。 如果您在编写文件时遇到问题,您可以随时循环结果(分页)并将其追加到CSV文件中

尝试之后使用PHPExcel转换为.xsl或.odf格式,否则将其保留为CSV。

你有没有尝试旧的梨Excel(又名Spreadsheet_Excel_Writer: http : //pear.php.net/package/Spreadsheet_Excel_Writer/redirected )?

Checkuout讨论关于梨VS PHPExcel:
http://phpexcel.codeplex.com/discussions/240688