8

レポートを Excel 形式でエンド ユーザーにエクスポートする必要があるいくつかのシステムを開発および保守しています。レポートは MySQL データベースから簡単な処理で収集され、通常は 10 ~ 15 列で最大 40,000 行のデータになります。データ量は着実に増加すると予想されます。

現時点では、Excel の生成に PHPExcel を使用していますが、もう機能していません。5000 行を超えると、メモリ消費と読み込み時間が耐えられなくなり、PHP のメモリ使用量とスクリプト実行時間の最大制限を無期限に増やしても解決できなくなります。データの処理は可能な限り無駄がなく、全体的な問題は PHPExcel がメモリを大量に消費することにあります。CSV 生成は軽くなりますが、残念ながら、ユーザーの要求により、サービスから Excel (および Excel のみ) をエクスポートする必要があります。これはフォーマット要件などによるものであるため、CSV はオプションではありません。

サードパーティのアプリケーション/モジュール/サービス/大規模なExcelを生成するためのアイデア/推奨事項はありますか? 商用ライセンスであるかどうかは問題ではありませんが、それが私たちのニーズに適合し、既存の PHP アプリケーションに統合でき、その役割を果たしている限りは問題ありません。私たちのサービスは通常、linux/php/mysql で実行されており、サーバーで必要なことはほぼ何でも実行できます。

ありがとう!

4

6 に答える 6

3

このような大量のデータについては、メモリ要件があるため、PHPExcelやApachePOI(Java用)などのツールはお勧めしません。私は最近同様のタスクに苦労しており、スプレッドシートにデータを挿入するための便利な(しかし少し面倒な)方法を見つけました。サーバーサイドでのExcelスプレッドシートの生成または更新は、単純なXML編集で実現できます。XLSXスプレッドシートをサーバーに置いており、dBからデータを収集するたびに、phpを使用して解凍します。次に、挿入する必要のあるワークシートの内容を保持している特定のXMLファイルにアクセスし、データを手動で挿入します。その後、スプレッドシートフォルダを圧縮して通常のXLSXファイルとして配布します。プロセス全体は非常に高速で信頼性があります。明らかに、XLSX / Open XMLファイルの内部編成に関連する問題や不具合はほとんどありません(例:Excelは、すべての文字列を別々のテーブルに格納し、このテーブルへの参照をワークシートで使用する傾向があります)。しかし、数字や文字列などのデータのみを挿入する場合、それほど難しくはありません。誰かが興味を持っているなら、私はいくつかのコードを提供することができます。

さて、これのサンプルコードを次に示します。私はそれが何をするのかコメントしようとしましたが、さらに説明を求めてください。

<?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;           
    }
}
?>   
于 2012-05-31T08:39:13.087 に答える
1

私が最新の状態に保とうとしているPHPExcelの代替品のリストはこちらです

PHPExcel が提供できる以上の生の速度/メモリ パフォーマンスを求めている場合、私が実際に推奨するのはIlia のlibXL用ラッパー拡張機能だけです。ライブラリはまだ積極的にサポートされているためです。

于 2012-05-18T12:16:16.087 に答える
0

テーブルを印刷するだけではどうですか?

<?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>";
于 2013-06-07T12:35:45.347 に答える
0

古い Pear Excel (別名 Spreadsheet_Excel_Writer: http://pear.php.net/package/Spreadsheet_Excel_Writer/redirected )を試しましたか?

Pear 対 PHPExcel に関するチェックアウト ディスカッション:
http://phpexcel.codeplex.com/discussions/240688

于 2012-05-18T08:21:21.650 に答える
0

CSV 形式でエクスポートできます。Excel で処理できます。ファイルの書き込みに問題がある場合は、いつでも結果をループして (ページネーション)、CSV ファイルに追加できます。

その後、PHPExcel を使用して .xsl または .odf 形式に変換してみてください。それ以外の場合は CSV のままにしてください。

于 2012-05-18T08:21:33.873 に答える
0

OfficeWriterをチェックしてください。最近、フォーチュン 500 の金融会社の大規模なデータセットのパフォーマンスを具体的に改善しました。特に必要なファイル形式 (グラフなど) よりもはるかに多くのことを行いますが、API は非常に使いやすく、評価を使用すると、POC をすばやく取得できます。免責事項 - 私は最新バージョンを構築したエンジニアです。

皆さんにとってのもう 1 つの欠点は、それが .NET であることです。

于 2012-05-18T15:31:32.967 に答える