1 Dernière modification par 20100 (13-01-2011 13:54:08)

Sujet : [Docs] Attente lors de la génération d'un flux de données (Excel, PDF)

Message d'attente pendant la génération d'un flux de données (Excel, PDF, ...)


1 - Introduction

1.1 - Motivations

Il est souvent utile d'afficher un message d'attente pendant la génération d'un document côté serveur afin d'informer l'utilisateur de l'état : en cours de traitement, échec ou succès. On prendra l'exemple d'une génération d'un document Excel avec la librairie phpExcel, mais ceci pourrait être adapté à n'importe quel long traitement php générant un flux de données à envoyer au navigateur.


1.2 - Présentation de la solution

Par l'envoi d'une requête Ajax, il est possible d'identifier un succès ou un échec d'une requête HTTP simple, mais certainement pas dans le cas d'un flux de données à générer. On sera dans l'incapacité d'utiliser le flux de données généré dans l'attribut réponse de notre instance Ajax, on mieux au aura dans une string le contenu du flux de données sans possibilité de lui préciser le type du header dans le document.

Ainsi la principale difficulté réside dans le cas d'un flux de données complexe à détecter, coté client donc en JavaScript, ce que l'on va appeler l'événement onFinishTraitement, et l'astuce (simple une fois établie) consiste à interroger une variable de session renseignant le statut de la progression du traitement par appel Ajax récursif. On pourra même avec ce mécanisme, mettre en place une progressbar PHP, JavaScript. Toutes les personnes qui se sont déjà penché sur cette question d'implémenter une véritable progressbar peuvent mesurer cette difficulté, mais l'article concerne uniquement à la présentation de ce mécanisme onFinishTraitement.


1.3 - Code source à télécharger de la démonstration

Le zip ci-dessous contient les codes sources de la démonstration avec la gestion d'un retardateur qui s'incrémente pour lancer les requête Ajax ainsi que d'un mode debug pour contrôler dans le navigateur le lancement des requêtes Ajax.

Télécharger les sources de cette démonstration v0.2.1
- gestion d'un mode de debug
- lancement des requêtes AJAX avec un retardateur incrémenté dans le temps

Cette démonstration nécessite les librairies PHP phpExcel et MPDF, ainsi que la librairie JavaScript JQuery, qui ne sont pas forcement présente dans le zip. Voici les récommandations de version et qui ont été testés à utiliser pour faire fonctionner la démonstration :

2 Dernière modification par 20100 (13-01-2011 13:25:44)

Re: [Docs] Attente lors de la génération d'un flux de données (Excel, PDF)

2 - Les éléments XHTML pour l'interface

2.1 - Boutons pour générer les documents

On propose la liste ci-dessous pour pointer vers la génération des différents fichiers. On aurait tout aussi bien pu écrire directement des liens vides. Les éléments de la liste possède tous la classe .generer qui va nous servir de sélecteur avec Jquery ainsi qu'un identifiant unique.

<ul id='blocLienGenerer'>
    <li><p><span class='generer' id='PDF'>Générer un PDF avec la librairie de phpExcel</span></p></li>
    <li><p><span class='generer' id='MPDF'>Générer un PDF avec la librairie MPDF</span></p></li>
    <li><p><span class='generer' id='Excel5'>Générer un document Excel5</span></p></li>
</ul>

2.2 - Bloc d'informations

On ajoute ensuite une div #loading qui va contenir l'image de chargement, du texte pour informer l'utilisateur sur l'état du chargement et une div optionnel pour afficher les informations de debug.

<div id="loading">
    <div id='loading-image'></div>
    <div id='loading-content'></div>
</div>
<div id='loading-debug'></div>

Les blocs div #loading et #loading-debug sont cachés en CSS par défaut

#loading{display:none;}
#loading-debug{display:none;}

Au lancement de la génération, on obtient le résultat suivant :

http://img4.hostingpics.net/pics/437235waitingMsgOnGeneratedFlux01.png

A la fin du traitement, on obtient le résultat suivant :

http://img4.hostingpics.net/pics/346424waitingMsgOnGeneratedFlux02.png



2.3 - Conteneur du script générant le flux de données (Excel, PDF, ...)

Enfin on place une iframe vide qui servira de conteneur pour charger le fichier à générer.

<iframe id="iframePhpExcel" src=""></iframe>

L'iframe est caché en CSS par défaut

#iframePhpExcel{visibility:hidden;position:absolute;}


3 - Génération du flux de données avec phpExcel côté serveur

3.1 - Script phpExcel_generation.php

Concernant la génération du flux de données, j'ai choisi d'utiliser la librairie phpExcel, je ne vais pas rentrer dans les détails sur la manière d'utiliser cette librairie.

Le script phpExcel_generation.php attend une variable passé en GET. Il s'agit de la variable idGenerer qui précise quel type de document doit être généré avec la librairie phpExcel. Il peut s'agir d'un document généré avec la librairie PDF par défaut de phpExcel à savoir TCPDF, il peut s'agir d'une librairie ajouter MPDF ou bien d'un fichier Excel5. La librairie MPDF est généralement utilisé pour des raisons de performances et prendre en compte le no-border. Le document généré utilise des données aléatoires créés pour remplir le document. On va voir séquentiellement, étape par étape les instructions de ce script.

On commence le script par créer une variable de session qui va prendre la valeur 'progress' pour indiquer que le traitement (la génération du flux de données est en cours). Puis tout de suite après on termine la session courante avec l'instruction session_write_close(); et ainsi on force le stockage de la variable de session pour qu'elle puisse être lu dans un autre script php (generationStatus.php) lance en Ajax retardé récursif.

3.2 - Initialisation variable de session

Le script commence par la déclaration de la variable de session generationStatut qui va renseigner si la génération du document est en cours ou fini.

@session_start();  
$_SESSION['generationStatut'] = "progress";

3.3 - Fermeture de la session courante

On ferme alors la session courrante immédiatement après, afin de permettre à d'autres script php de pouvoir lire cette variable de session. La fonction session_write_close termine la session courante, après avoir stocké les données, autrement dit on force le stockage de la variable de session pour qu'ellee puiss être utilisé dans le script generationStatus.php lancé en AJAX retardé récursif.

3.4 - Instanciation de phpExcel

On instancie l'objet phpExcel à partir duquel la librairie phpExcel va générer le document. La suite concerne la génération du flux de données via la librairie phpExcel. L'instance $objPHPExcel contient toutes les informations nécessaires à la génération du flux. (je ne rentre pas dans les détails d'utilisation de phpExcel ici)

include_once(dirname(__FILE__)."/lib/php/phpExcel/Classes/PHPExcel.php");
$objPHPExcel = new PHPExcel();
   //(instructions pour générer le document)

3.5 - Instanciation du writer de phpExcel

On choisi ensuite le constructeur du flux de données ($objWriter) selon le paramètre qui définit le mode choisi du flux de données, passé en GET. Un des nombreux avantages de la librairie phpExcel est de fournir une fabrique permettant de générer aisément différent type de flux.


$mode = (isset($_GET['mode'])) ? $_GET['mode'] : null;
$date = date("Y_m_d_H.i.s");
$nomFichier = "test_".$date;
switch ($mode) {
 
    // Librairie PDF de phpExcel par défaut utilisé (basé sur TCPDF)
    case 'PDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_PDF($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        break;
 
    // On utilise plutot MPDF pour regler les problèmes de no-border et amélioré les performances
    case 'MPDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_HTML($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        $objWriter->setUseInlineCSS(true);
        ob_clean();
        $html = $objWriter->generateHTMLHeader();
        $html .= $objWriter->generateStyles();
        $html .= $objWriter->generateSheetData();
        $html .= $objWriter->generateHTMLFooter();
        include_once(dirname(__FILE__)."/lib/php/MPDF46/mpdf.php");
        $mpdf = new mPDF();
        $mpdf->WriteHTML($html);           
        break;
 
    // Génération du document en version Excel5
    Case 'Excel5':
        $objWriter = new PHPExcel_Writer_Excel5($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);   
        break;
 
    default:
        echo "<pre>Le mode de génération choisi <b>(.".$mode.".)</b> n'existe pas.</pre>";
}

3.6 - Mise à jour de la variable de session

On relance alors la session, afin de changer la valeur de session generationStatut, pour indiquer que la génération du flux de données côté serveur est terminé. Afin que le script php generationStatus.php lancé en Ajax retardé récursif puisse indiqué à l'utilisateur la fin du traitement (onFinishTraitement)

session_start();
$_SESSION['generationStatut'] = "fini";

3.7 - Envoie du fichier généré au navigateur

On envoie le fichier au navigateur en précisant les headers en fonction du fichier généré. A la fin, il suffit donc maintenant d'envoyer le flux de données généré au navigateur en lui précisant les headers du documents. Un appel Ajax traditionnel d'un script php permettrait de récuperer une chaîne de caractères auquel il nous serait impossible de lui préciser qu'il s'agit bien d'un document PDF par exemple.

ob_clean();
switch ($mode) {
    case 'PDF':
        header('Content-type: application/pdf');
        header("Content-Disposition: attachment; filename=".$nomFichier.".pdf");
        header('Cache-Control: max-age=0');   
        $objWriter->save('php://output');   
        break;
    case 'MPDF':
        $mpdf->Output($nomFichier.".pdf",D);       
        break;
    Case 'Excel5':
        header('Content-type:application/vnd.ms-excel');
        header('Content-Disposition:inline;filename='.$nomFichier.'.xls');
        header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date dans le passé
        $objWriter->save('php://output');
        break;
}

3.8 - Script complet de phpExcel_generation.php

Au final le script complet est :

<?php
 
//------------------------------------------------------------------.
// Variable de session pour informer sur le statut de la génération |
//------------------------------------------------------------------'
@session_start(); 
$_SESSION['generationStatut'] = "progress"; 
 
 
//----------------------------------------------------------------------------.
// Termine la session courante, après avoir stocké les données, autrement dit |
// on force le stockage de la variable de session pour qu’elle puisse être    |
// utilisé dans le script generationStatus.php lancé en AJAX retardé récursif |
//----------------------------------------------------------------------------'
session_write_close();
 
 
//------------------------------.
// Création de l'objet PHPExcel |
//------------------------------'
include_once(dirname(__FILE__)."/lib/php/phpExcel/Classes/PHPExcel.php");
$dateGeneration = date('d/m/Y \à H:i:s');
$objPHPExcel = new PHPExcel();
    $objPHPExcel->setActiveSheetIndex(0);
    $objPHPExcel->getDefaultStyle()->getFont()->setName('Calibri');
    $objPHPExcel->getDefaultStyle()->getFont()->setSize(10);
    $activeSheet = $objPHPExcel->getActiveSheet();
    $activeSheet->setTitle('Exemple de generation phpExcel');
 
    // Header document PHPExcel
    $activeSheet->setCellValue('A1',utf8_encode("Suivi des ventes et contrôle"));
    $activeSheet->setCellValue('B3',"Pays");
    $activeSheet->setCellValue('B4',utf8_encode("Contexte"));
    $activeSheet->setCellValue('B5',"Dates des ventes");
    $activeSheet->setCellValue('B7',utf8_encode("Nombre de vendeur dans le pays"));
    $activeSheet->setCellValue('B8',utf8_encode("Nombre d'usine dans le pays"));
    $activeSheet->setCellValue('B9',utf8_encode("Nombre de publicité dans le pays"));
    $activeSheet->setCellValue('B10',utf8_encode("Taux de vente"));
    $activeSheet->setCellValue('B11',utf8_encode("Taux de rentabilité"));
    $activeSheet->setCellValue('C3',utf8_encode("Espagne"));
    $activeSheet->setCellValue('C4',utf8_encode("Vente d'équipement"));
    $activeSheet->setCellValue('C5',utf8_encode("Du 01/01/2010 au 05/02/2010"));
    $activeSheet->setCellValue('E7',71);
    $activeSheet->setCellValue('E8',37);
    $activeSheet->setCellValue('E9',16);
    $activeSheet->setCellValue('E10',utf8_encode("52 %"));
    $activeSheet->setCellValue('E11',utf8_encode("23 %"));
    $activeSheet->setCellValue('B12',utf8_encode("Document généré le ".$dateGeneration));
    $activeSheet->mergeCells('A1:G1');
    $activeSheet->mergeCells('C3:D3');
    $activeSheet->mergeCells('C4:D4');
    $activeSheet->mergeCells('C5:D5');
    $activeSheet->mergeCells('B7:D7');
    $activeSheet->mergeCells('B8:D8');
    $activeSheet->mergeCells('B9:D9');
    $activeSheet->mergeCells('B10:D10');
    $activeSheet->mergeCells('B11:D11');
    $activeSheet->mergeCells('B12:D12');
 
    // Mise en page du Header document PHPExcel
    $activeSheet->getStyle('A1:G1')->applyFromArray(
        array(
            'font' => array('bold' => true, 'size'=>16),
            'alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER),
            'borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)),
            'fill'=>array('type'=>PHPExcel_Style_Fill::FILL_SOLID,'color'=>array('argb'=>'FF99CCFF'))
        )
    );
    $activeSheet->getStyle('B12')->applyFromArray(array('font' => array('italic' => true)));
    $activeSheet->getStyle('B2:B11')->applyFromArray(array('font' => array('bold' => true)));
    $activeSheet->getStyle('B3:D5')->applyFromArray(
        array('borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)))
    );
    $activeSheet->getStyle('B7:E11')->applyFromArray(
        array('borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)))
    );
    $activeSheet->getStyle('E7:E11')->applyFromArray(
        array('alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER))
    );
 
    // Body document PHPExcel
    $activeSheet->setCellValueByColumnAndRow(0, 14, "Ident");
    $activeSheet->setCellValueByColumnAndRow(1, 14, "Nom");
    $activeSheet->setCellValueByColumnAndRow(2, 14, "Secteur");
    $activeSheet->setCellValueByColumnAndRow(3, 14, "Nature");
    $activeSheet->setCellValueByColumnAndRow(4, 14, "Ref");
    $activeSheet->setCellValueByColumnAndRow(5, 14, "Date\n".utf8_encode("Réponse"));
    $activeSheet->setCellValueByColumnAndRow(6, 14, "Controle\n".utf8_encode("des produits"));
    $activeSheet->getStyle('A14:G14')->applyFromArray(
        array(
            'font' => array('bold' => true),
            'alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER),
            'borders'=>array('allborders'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN)),
            'fill'=>array('type'=>PHPExcel_Style_Fill::FILL_SOLID,'color'=>array('argb'=>'FF99CCFF'))
        )
    );
    $activeSheet->getStyle('F14')->getAlignment()->setWrapText(true);
    $activeSheet->getStyle('G14')->getAlignment()->setWrapText(true);
    $activeSheet->getStyle('A14:G14')->getAlignment()->setVertical(PHPExcel_Style_Alignment::VERTICAL_TOP);
    $arrayNatureLibelle = array('Agroalimentaire', 'Usine', 'Université');
    $arrayNatureCode = array(151,152,153,168,198,286,292,336,541,542,543,556);
    $ligne = 15;
    for($i=0;$i<71;$i++) {
        $rneEcole = "XID".($i+10);
        $nom = "VENTE ".$i;
        $secteur = (rand(0,1)==0) ? 'GLOBAL' : 'ATOME';
        $natureLibelle = $arrayNatureLibelle[array_rand($arrayNatureLibelle,1)];
        $natureCode = $arrayNatureCode[array_rand($arrayNatureCode,1)];
        $dateReponse = date("d/m/Y",strtotime("2010-01-01 + "
            .rand(0,round((strtotime('2010-02-05')-strtotime('2010-01-01'))/(60*60*24)))." days"));
        $campagneValiderEcole = (rand(0,1)==0) ? 'OUI' : 'NON';
        $activeSheet->setCellValueByColumnAndRow(0, $ligne, utf8_encode($rneEcole));
        $activeSheet->setCellValueByColumnAndRow(1, $ligne, utf8_encode($nom));
        $activeSheet->setCellValueByColumnAndRow(2, $ligne, utf8_encode($secteur));
        $activeSheet->setCellValueByColumnAndRow(3, $ligne, utf8_encode($natureLibelle));
        $activeSheet->setCellValueByColumnAndRow(4, $ligne, utf8_encode($natureCode));
        $activeSheet->setCellValueByColumnAndRow(5, $ligne, utf8_encode($dateReponse));
        $activeSheet->setCellValueByColumnAndRow(6, $ligne, utf8_encode($campagneValiderEcole));
        if(strcmp($campagneValiderEcole,'OUI')==0) {
            $activeSheet->getStyle('A'.$ligne.':G'.$ligne)->applyFromArray(
                array('fill'=>array('type'=>PHPExcel_Style_Fill::FILL_SOLID,'color'=>array('argb'=>'FFCCFFCC')))
            );
        }
        $ligne++;
    }
    $activeSheet->getStyle('A15:G'.($ligne-1))->applyFromArray(
        array(
            'borders'=>array(
                'left'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'right'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'bottom'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'vertical'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  ),
                'horizontal'=>array('style'=>PHPExcel_Style_Border::BORDER_THIN  )
            )       
        )
    );
    $styleColonneCentrer = array('alignment' => array('horizontal' => PHPExcel_Style_Alignment::HORIZONTAL_CENTER));
    $activeSheet->getStyle('C15:C'.($ligne-1))->applyFromArray($styleColonneCentrer);
    $activeSheet->getStyle('E15:G'.($ligne-1))->applyFromArray($styleColonneCentrer);
    $activeSheet->getColumnDimension('A')->setWidth(12);
    $activeSheet->getColumnDimension('B')->setWidth(40);
    $activeSheet->getColumnDimension('C')->setWidth(12);
    $activeSheet->getColumnDimension('D')->setWidth(40);
    $activeSheet->getColumnDimension('E')->setWidth(6);
    $activeSheet->getColumnDimension('F')->setWidth(12);
    $activeSheet->getColumnDimension('G')->setWidth(22);
 
    // Set Zone d'impression
    $activeSheet->getPageSetup()->setPrintArea('A1:G'.($ligne));
    $activeSheet->getPageSetup()->setOrientation(PHPExcel_Worksheet_PageSetup::ORIENTATION_LANDSCAPE);
    $activeSheet->getPageSetup()->setPaperSize(PHPExcel_Worksheet_PageSetup::PAPERSIZE_A4);
    $activeSheet->getPageMargins()->SetLeft(0.1);
    $activeSheet->getPageMargins()->SetRight(0.1);
    $activeSheet->getPageMargins()->setTop(0.4);
    $activeSheet->getPageMargins()->setBottom(0.6);
    $activeSheet->getPageMargins()->setHeader(0.1);
    $activeSheet->getPageMargins()->setFooter(0.4);
    $activeSheet->getHeaderFooter()->setOddHeader("&C&HAPPLICATION v.0.1");
    $activeSheet->getHeaderFooter()->setOddFooter('&L&BGeneration flux de donnees&RPage &P / &N');
    $activeSheet->getPageSetup()->setHorizontalCentered(true);
    $activeSheet->getPageSetup()->setVerticalCentered(false);
    $activeSheet->getPageSetup()->setRowsToRepeatAtTopByStartAndEnd(14, 14);
 
 
//-----------------------------------------------------.
// Création du writer en fonction du moge passé en GET |
//-----------------------------------------------------'
$date = date("Y_m_d_H.i.s");
$mode = (isset($_GET['mode'])) ? $_GET['mode'] : null;
$nomFichier = "test_".$date;
switch ($mode) {
    // Librairie PDF de phpExcel par défaut utilisé (basé sur TCPDF)
    case 'PDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_PDF($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        break;
    // On utilise plutot MPDF pour regler les problèmes de no-border et amélioré les performances
    case 'MPDF':
        ini_set('memory_limit', '-1');
        $objWriter = new PHPExcel_Writer_HTML($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);
        $objWriter->setUseInlineCSS(true);
        ob_clean();
        $html = $objWriter->generateHTMLHeader();
        $html .= $objWriter->generateStyles();
        $html .= $objWriter->generateSheetData();
        $html .= $objWriter->generateHTMLFooter();
        include_once(dirname(__FILE__)."/lib/php/MPDF46/mpdf.php");
        $mpdf = new mPDF();
        $mpdf->SetHeader(
            array (
                'L' => array(
                    'content' => '',
                    'font-size' => 10,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'C' => array(
                    'content' => ' Exemple',
                    'font-size' => 9,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'R' => array(
                    'content' => '',
                    'font-size' => 10,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'line' => 1
            ),
            'O'
        );
        $mpdf->SetFooter(
            array (
                'L' => array(
                    'content' =>
                    'Suivi de validation des directeurs',
                    'font-size' => 9,'font-style' => '','font-family' => 'FreeSerif','color'=>'#000000'),
                'C' => array(
                    'content' => '',
                    'font-size' => 9,'font-style' => 'B','font-family' => 'FreeSerif','color'=>'#000000'),
                'R' => array(
                    'content' => 'Page {PAGENO} / {nbpg}',
                    'font-size' => 9,'font-style' => '','font-family' => 'FreeSerif','color'=>'#000000'),
                'line' => 1
            ),
            'O'
        );
        $mpdf->WriteHTML($html);           
        break;
    // Génération du document en version Excel5
    Case 'Excel5':
        $objWriter = new PHPExcel_Writer_Excel5($objPHPExcel);
        $objWriter->setPreCalculateFormulas(false);   
        break;
    default:
        echo "<pre>Le mode de génération choisi <b>(.".$mode.".)</b> n'existe pas.</pre>";
}
 
 
//-----------------------------------------------.
// Informe que la génération du fichier est fini |
//-----------------------------------------------'
session_start();
$_SESSION['generationStatut'] = "fini";
 
 
//--------------------------------------------------.
// Envoi le fichier au browser selon le mode choisi |
//--------------------------------------------------'   
ob_clean();
switch ($mode) {
    case 'PDF':
        header('Content-type: application/pdf');
        header("Content-Disposition: attachment; filename=".$nomFichier.".pdf");
        header('Cache-Control: max-age=0');   
        $objWriter->save('php://output');   
        break;
    case 'MPDF':
        $mpdf->Output($nomFichier.".pdf",D);       
        break;
    Case 'Excel5':
        header('Content-type:application/vnd.ms-excel');
        header('Content-Disposition:inline;filename='.$nomFichier.'.xls');
        header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
        header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Date dans le passé
        $objWriter->save('php://output');
        break;
}   
 
?>


4 - Lecture de la variable de session côte serveur avec generationStatus.php

4.1 - Script generationStatus.php

Le script generationStatus.php est tout simple, il s’agit simplement de faire un echo de la variable de session qui renseigne sur l’avancée du traitement. Ce script sera par la suite appellé en JavaScript afin d'atteindre l'évenement onFinishTraitement.

<?php 
    session_start();
    echo $_SESSION['generationStatut'];
?>


5 - Lecture de la variable de session par appel AJAX retardé recursif

5.1 - Chargeur JQuery

On utilise la librairie JQuery pour nous simplifier la vie et avec JQuery on commence toujours par attendre que l'arbre DOM soit entièrement chargé avec l'appel de l'instruction $(document).ready(function(){...});.

On utlise ensuite le sélecteur sur la classe .generer avec $('.generer'), et pour chaque élément selectionné on ajoute un listeneur sur l'évenement clik.

$(document).ready(function(){
    $('.generer').each(function() {
        $(this).click(
            function(){
                (...)
            }
        );
    });
});

5.2 - Mise en oeuvre du listener onClick sur les boutons de l'interface

En JavaScript sur l’événement de clic pour lancer la génération du fichier :

  • La première instruction est de récupère l'identifiant de l'élément cliqué (PDF, MPDF ou Excel5)

  • On informe l'utilisateur que la génération du fichier est en cours en affichant une image et du texte. Le chargement de l'image dans #loading-image se fait simplement par l'ajout de la classe .chargement dans l'élément

  • On charge dans l’iframe le script php phpExcel_generation.php?mode=idGenerer qui génère le document PDF, MPDF ou Excel5

  • On fait un appel retardé en utilisant setTimeout sur la fonction getGenerationStatus. On remarque que pour pouvoir appellé une fonction avec des paramètres dans setTimeout, il est nécessaire d'utiliser une fonction anonyme.

$(document).ready(function(){
 
    $('.generer').each(function() {
        $(this).click(
            function(){               
                // Récupération de l'identifiant du fichier à générer surlequel l'utilisateur à cliquer
                var idGenerer = $(this).attr('id');
 
                // Informe l'utilisateur que la génération du fichier est en cours
                var stringTemp = "<p>Génération du fichier "+idGenerer+" en cours...</p><p>";
                $("#loading-content").html(stringTemp);
                $("#loading-image").removeClass("information");
                $("#loading-image").addClass("chargement");
                $("#loading").css("display","block");
                $("#loading-debug").html("");
                $("#loading-debug").css("display","none");
 
                // Chargement de l'iframe associé à l'idGenerer clické
                var oIFrm = document.getElementById("iframePhpExcel");
                oIFrm.contentWindow.document.location = "phpExcel_generation.php?mode="+idGenerer;
 
                // Appel de la fonction récursive pour tester le statut après une attente d'une seconde.
                var timeWait = 1000;
                var timeFactor = 2;
                var timeWaitMax = 8000;
                var timeOut = 30000;
                var timeExec = 1000;
                var nbAppel = 0;                   
                setTimeout(
                    function() {
                        getGenerationStatus(timeWait, timeFactor, timeWaitMax, timeOut, timeExec, nbAppel, idGenerer, true);
                    }
                    , timeWait
                );
            }
        );
    });
 
});

5.3 - Appel AJAX récursif retardé

La fonction getGenerationStatus effectue un appel AJAX récursif retardé. L'appel AJAX se fait de manière asynchrone sur la page phpExcel_generationStatus.php et cette fonction prend en compte plusieurs paramètres :

  • param (int) timeWait : Temps en ms d'attente avant de relancer une requête AJAX

  • param (int) timeFactor : Facteur pour incrémenter le temps avant le lancement récursif

  • param (int) timeWaitMax : Temps en ms d'attente maximum avant de relancer une requête AJAX

  • param (int) timeOut : Temps en ms d'attente global maximum

  • param (int) timeExec : Temps d'execution

  • param (int) nbAppel : Nombre d'appel récursif effectué

  • param (String) idGenerer : Type de fichier generer (PDF, MPDF, Excel5)

  • param (Bool) debug : True affiche le debug

Avec cette fonction on repond à la question suivante : est-ce que la génération du document est terminé ?

Tant que la réponse est négative, le fichier est en cours de génération et la relance de cette question ne se fait qu'a intervalle de temps timeWait qui s'incremente à chaque appel à l'aide d'un facteur timeFactor. Ainsi on interroge fréquement le serveur au début de la génération, et le temps d'attente s'agrandit d'appel en appel avant d'atteindre un temps maximum d'attente timeWaitMax. Si le nombre d'appel est trop important, un arret est prevu à l'aide d'un temps d'execution maximum timeOut. Il est aussi possible d'afficher des informations sur les appels effectués si le paramètre debug vaut true.

/**
* param (int) timeWait : Temps en ms d'attente avant de relancer une requête AJAX
* param (int) timeFactor : Facteur pour incrémenter le temps avant le lancement récursif
* param (int) timeWaitMax : Temps en ms d'attente maximum avant de relancer une requête AJAX
* param (int) timeOut : Temps en ms d'attente global maximum
* param (int) timeExec : Temps d'execution
* param (int) nbAppel : Nombre d'appel récursif effectué
* param (String) idGenerer : Type de fichier generer (PDF, MPDF, Excel5)
* param (Bool) debug : True affiche le debug
*/

function getGenerationStatus(timeWait, timeFactor, timeWaitMax, timeOut, timeExec, nbAppel, idGenerer, debug) {
    nbAppel = nbAppel+1;
    timeExec = timeExec+timeWait;
    var timeWaitCurrent = timeWait*timeFactor;
    if(timeWaitCurrent>timeWaitMax) {
        timeWaitCurrent = timeWaitMax;
    }
 
    if(timeExec<timeOut) {
        $.ajax({
            url: "phpExcel_generationStatus.php",
            type: "POST",
            success: function(data) {
                if(data=="progress") {
                    var stringTemp = "<p>Génération du fichier "+idGenerer+" en cours...<p/>";
                    $("#loading-content").html(stringTemp);
                    if(debug) {
                        var stringTempDebug =
                        "<p class='monospace'>"+
                            "--> Numéro d'appel = "+nbAppel+
                            "<br/> --> Temps d'attente avant le prochain appel = "+timeWaitCurrent/1000+" sec"+
                            "<br/> --> Temps d'execution total = "+timeExec/1000+" sec"+
                        "</p><p class='monospace'>Return statut : progress</p>";
                        $("#loading-debug").append(stringTempDebug);
                        $("#loading-debug").css("display","block");
                    }
                    setTimeout(
                        function() {
                            getGenerationStatus(
                                timeWaitCurrent, timeFactor, timeWaitMax, timeOut, timeExec, nbAppel, idGenerer, true);
                            }
                        , timeWaitCurrent
                    );
                } else {
                    if(debug) {
                        var stringTempDebug =
                        "<p class='monospace'>"+
                            "--> Numéro d'appel = "+nbAppel+
                            "<br/> --> Temps d'execution total = "+timeExec/1000+" sec"+
                        "</p><p class='monospace'>Return statut : fini</p>";
                        $("#loading-debug").append(stringTempDebug);
                        $("#loading-debug").css("display","block");
                    }
                    var stringTemp = "<p>Génération du fichier "+idGenerer+" terminé<p/>";
                    $("#loading-content").html(stringTemp);
                    $('#loading-image').addClass('information');
                }
            }
        });
    } else {
        if(debug) {
            var stringTempDebug =
            "<p class='monospace'>"+
                "--> Numéro d'appel = "+nbAppel+
                "<br/> --> Temps d'execution total = "+timeExec/1000+" sec"+
            "</p><p class='monospace'>TimeOut atteint</p>";
            $("#loading-debug").append(stringTempDebug);
            $("#loading-debug").css("display","block");
        }
        var stringTemp = "<p>TimeOut atteint.<p/>";
        $("#loading-content").html(stringTemp);
        $('#loading-image').addClass('information');
    }
}

3

Re: [Docs] Attente lors de la génération d'un flux de données (Excel, PDF)

Oulala, ca a l'air prometteur tout ca smile

lesCigales.ORG sysadm1n
L'hébergeur qui sent bon la lavande - Un control panel qu'il est chouette - Viendez nous parler!
"All that is necessary for evil to triumph is for good men to do nothing."

4

Re: [Docs] Attente lors de la génération d'un flux de données (Excel, PDF)

^^
Bon voila, j'ai fini la mise en page, en espérant avoir été assez clair.

Le code fourni a été testé, mais je ne garanti pas l'absence de coquilles dans le code...