entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

3votos

Progreso de creación de archivo PHP+Ajax

Hola, recién un compañero me pregunto si es posible mostrar el proceso de creación de un archivo (PDF) en vivo para que el cliente sepa que se esta procesando la solicitud.

De he estado dando vuelta al asunto y se me han ocurrido diferentes formas pero ninguna me convence del todo. El principio que tengo es:

  • El archivo crear.php?pid=aaa se el el encargado de crear el archivo e indica el total de pasos y el paso actual en el que se encuentra.
  • El archivo proceso.php?pid=aaa lee el total de pasos y el paso actual en el que se encuentra.
  • Notar que son procesos syncronicos

De lo que llevo pensado es.

  1. Manejar una variable de secesión (pero creo que no se puede escribir y leer al mismo tiempo)
  2. Crear un archivo temporal y que se escriba en el y cada vez que se incremente un paso se actualiza en crear.php y proceso únicamente lee.
  3. Utilizar los cookies (este no lo he echo y no creo que funcione).

Si existe un modo de hacer esto me justaria que me comentaran cuales conocen.
Tambien en caso de no ser factible desarrollar esto argumentar el por que.

0voto

Leonardo-Tadei comentado

Hola @MrGenis,

por favor, agregá a tu pregunta una descripción del proceso de creación del PDF, para conocer mejor el problema.
Es muy distinto si la creación son una serie de pasos manuales de cosas que el usuario va cargando, a que sea el proceso de generar un PDF aprentando un botón en base a varias opciones seleccionadas.

0voto

MrGenis comentado

Hola, me tarde un poco :P, los datos que el usuario ingresa son independientes del proceso ya que estos son tratados previamente a la creación del archivo.
En si es lo siguiente:

  1. Se obtienen n registros de una base de datos (se definen la cantidad de pasos tPasos)
  2. Se inicia el PDF (TCPDF).
  3. Un for que va creando la tabla con los datos obtenidos de la base de datos. (aquí es cuando se actualiza el paso pActual)

Con cada ciclo que complete el for actualiza de alguna forma el contador con la clave pid=aaa y poder ser leída en proceso.php.

3 Respuestas

2votos

blaDDI Puntos2260

Crear un archivo html/php con el siguiente código

<html>
   <head>
     <title></title>
   </head>
   <style>.modal{display:none;position:absolute;top:25%;left:25%;width:40%;height:15%;padding:16px;background:#fff;color:#333;z-index:1002;overflow:auto;}.overlay{display:none;position:absolute;top:0;left:0;width:100%;height:100%;background:#000;z-index:1001;opacity:.75;-moz-opacity:0.75;filter:alpha(opacity=75);}</style>
   <body>

     <center><a href="#" id="test">Generar Reporte</a></center>

     <div id="fade" class="overlay"></div>
    <div id="light" class="modal">
     <p>POR FAVOR ESPERE,GENERANDO SU REPORTE....</p>
    </div>

     <script type="text/javascript">
       document.getElementById('test').onclick = function() {

        document.getElementById('light').style.display='block';
        document.getElementById('fade').style.display='block'   

        var url = "file.php?datos_a_enviar=1&tipo=EXL";
        var filename = "file_name_exit";
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.responseType = 'blob';

        xhr.onload = function(){

            document.getElementById('light').style.display='none';//ocultar msj
            document.getElementById('fade').style.display='none'//ocultar msj   

            if(xhr.getResponseHeader("msj")==undefined){
                var a = document.createElement('a');
                a.href = window.URL.createObjectURL(xhr.response); // xhr.response es un blob file
                a.target = '_blank';
                a.download = filename; // nombre del archivo.
                a.style.display = 'none';
                document.body.appendChild(a);
                a.click();
                delete a;
                }
            else{SubMsg.show();}
        }// fin xhr.onload  
        xhr.send();
       };   
     </script>
   </body>
</html>

Luego crear un archivo php con el nombre de file.php (puede llevar otro
nombre pero debes cambiar estas lineas del anterior archivo var url = "file.php?datos_a_enviar=1&tipo=EXL";) con el siguiente código

<?php
echo $_GET['tipo'];
if($_GET['datos_a_enviar']=="1"):
 switch($_GET['tipo']):
    case 'EXL':
        header("Content-type: application/vnd.ms-excel; name='excel'");
        header("Content-Disposition: filename=ficheroExcel.xls");
        header("Pragma: no-cache");
        header("Expires: 0");
    break;
    case 'PDF':
        header("Content-type:application/pdf");
        header("Content-Disposition:attachment;filename='downloaded.pdf'");
        readfile("my_archivo_pdf.pdf");
    break;  
endswitch; 
else:
    header("msj:No_Hay_datos"); 
endif;
sleep(2);
?>

2votos

Leonardo-Tadei Puntos227320

Hola @MrGenis,

para hacer algo así, una opción podría ser que la generación del PDF se lance en backgrond a otro script via AJAX, y que ese otro script sea el que vaya devolviendo la información del progreso del proceso.

Del lado del cliente para ir mostrando los avances podrías usar una biblioteca como esta: http://www.ajaxshake.com/plugin/ES/446/dadcfc6b/agregar-una-barra-de-progreso-ajax-a-tu-sitio-jsprogressbarhandler.html

Lo único a tener en cuenta es que comola salida de proceso.php debe ser ininterrumpida, tendrás que quedarte con el último valor como el válido. Esto significa que los avances los tenés que ir leyendo a medida que aparecen, en lugar del caso típico de AJAX en que la respuesta se evalúa solo cuando termina el proceso.

Espero haberte orientado un poco!

PD: a veces, basta con que el usuario sepa que tiene que esperar y no importa saber exactamente por dónde va. En ese caso, solo hay que poner vsible algún GIF animado de círculo o barra avanzando y volviendo a empezar en un loop. Si te fijás, la mayoría de los sitios, incluso bancos, implementan este mecanismo...

0voto

MrGenis comentado

Realice unas pruebas usando SQLLite ya que descubrí que no puedo leer y escribir al mismo tiempo las variables de que están en sesión ( no he encontrado aun el porque ).

Lo siguiente es una abstracción de como seria la actualización del contador cuando se esta creando el PDF. crear.php

<?php
set_time_limit(0);
defined ('APLICATION_HTML')
|| define('APLICATION_HTML', realpath( dirname(__FILE__) . '/..'));

require_once APLICATION_HTML . "/../systems/femeka.php";
add_path(SYSTEM . '/app/library');
require_once SYSTEM . '/fmk-public.php';
require_once 'Source/Db/StepsDb.php';

$dbstep = new Source_Db_StepsDb(APPLICATION_FILES . '/process_database.s3db');
$proceso = $dbstep->addProsses(request()->getParam('pkey'), 50);

function output($val) {
    echo $val;
    flush();
    ob_flush();
    usleep( 250000 );
}

$dbstep->updateStep($proceso->id, 0);
$totalSteps = $proceso->steps;

output("<pre>0Begin... (counting to {$totalSteps})</pre>");
output("<code>");
for( $i = 0 ; $i < $totalSteps ; $i++ ) {
    output($i+1);
    $dbstep->updateStep($proceso->id, $i+1);
    $proceso->step = $i+1;
}
output("</code>");
output('<pre>End...</pre>');

echo "<pre>";
echo print_r($dbstep->getProsses($proceso->id), true);
echo "</pre>";

Este es el archivo que se encarga de leer los pasos actuales. proceso.php

<?php
set_time_limit(0);
defined ('APLICATION_HTML')
|| define('APLICATION_HTML', realpath( dirname(__FILE__) . '/..'));

require_once APLICATION_HTML . "/../systems/femeka.php";
add_path(SYSTEM . '/app/library');
require_once SYSTEM . '/fmk-public.php';
require_once 'Source/Db/StepsDb.php';

$dbstep = new Source_Db_StepsDb(APPLICATION_FILES . '/process_database.s3db');
$pkey = request()->getParam('pkey');
$proceso = $dbstep->getProsses($pkey);

function output($step, $total) {
    echo '{"step":' . $step . ', "total":' . $total . '}';
    flush ();
    ob_flush ();
    usleep ( 500000 );
}

header('Content-type: text/html; charset=utf-8');
if (!($proceso)) {
    echo '{"success":0, "message":"Progreso no disponible"}';
    exit;
}

while( $proceso->current < $proceso->steps ) {
    echo output($proceso->current, $proceso->steps);
    $proceso = $dbstep->getProsses($pkey);
};
echo '{"success":1, "message":"terminado"}';

Y el AJAX me queda de esta forma.

function leerProgreso() {
    last_response_len = false;
    $.ajax({
        async : true,
        url   : "<?php echo baseurl(); ?>/descarga/progreso.php?pkey=123458",
        xhrFields : {
            onprogress : function(e) {
                var this_responce,
                    response = e.currentTarget.response;
                    if (last_response_len === false) {
                        this_responce     = response;
                        last_response_len = response.length;
                    } else {
                        this_responce = response.substring(last_response_len);
                        last_response_len = response.length;
                    }
                    console.info(this_responce);
            }
        }
    }).done(function(data) {
        console.info("Respuesta completa\n" + data);
    }).fail(function() {
        console.erro("Error: \n", data);
    });
    console.info("Request Send");
}

0voto

Leonardo-Tadei comentado

Gracias por compartir los avances. Te está funcionando bien la barra de progreso?

Por otra parte, tal vez sea bueno crear una nueva pregunta por el tema de las variables de sesión que mencionás.

Saludos cordiales!

0voto

blaDDI Puntos2260
$(document).ready(function(){   
    $("#pdf").click(function(){

        // Aquí le muestro  un mensaje al usuario diciendo que se está generando su archivo             
        WinSnd.Message({type:'info', title:'Generando Reporte', value:'Por favor, Espere ...', buttons:' '}).show();

        var url="report.php?&var1=2&var2=5"+&datype='PDF'";//EXL

        var filename = "file_name";
        var xhr = new XMLHttpRequest();
        xhr.responseType = 'blob';
        var texto='';
        xhr.open('GET', url);
        xhr.send(); 

        xhr.onload = function(){
            WinSnd.hide();// aquí oculto el mensaje de generando reporte
            if(xhr.getResponseHeader("msj")==undefined){// compruebo que si existe una cabezera con el nombre de msj si existe significa que la consulta no arrojó valores                      
                var a = document.createElement('a');
                a.href = window.URL.createObjectURL(xhr.response); // xhr.response es un blob file
                a.target = '_blank';
                a.download = filename; // nombre del archivo.
                a.style.display = 'none';
                document.body.appendChild(a);
                a.click();
                delete a;
                //window.URL.revokeObjectURL(obj_url); para mostrar un pdf en iframe
            }
            else{SubMsg.show();}//Aquí muestro un mensaje si la consulta no arroja nada
        };// fin onload

    });//fin function
});// fin 

0voto

Peter comentado

¿Podrías explicar la solución que estas ofreciendo por favor?

Saludos.

0voto

blaDDI comentado

Guardar estas linea en un archivo html/php

<html>
       <head>
         <title></title>
       </head>
       <style>.modal{display:none;position:absolute;top:25%;left:25%;width:40%;height:15%;padding:16px;background:#fff;color:#333;z-index:1002;overflow:auto;}.overlay{display:none;position:absolute;top:0;left:0;width:100%;height:100%;background:#000;z-index:1001;opacity:.75;-moz-opacity:0.75;filter:alpha(opacity=75);}</style>
       <body>

         <center><a href="#" id="test">Generar Reporte</a></center>

         <div id="fade" class="overlay"></div>
        <div id="light" class="modal">
         <p>POR FAVOR ESPERE,GENERANDO SU REPORTE....</p>
        </div>

         <script type="text/javascript">
           document.getElementById('test').onclick = function() {

            document.getElementById('light').style.display='block';
            document.getElementById('fade').style.display='block'   

            var url = "file.php?datos_a_enviar=1&tipo=EXL";
            var filename = "file_name_exit";
            var xhr = new XMLHttpRequest();
            xhr.open('GET', url);
            xhr.responseType = 'blob';

            xhr.onload = function(){

                document.getElementById('light').style.display='none';//ocultar msj
                document.getElementById('fade').style.display='none'//ocultar msj   

                if(xhr.getResponseHeader("msj")==undefined){
                    var a = document.createElement('a');
                    a.href = window.URL.createObjectURL(xhr.response); // xhr.response es un blob file
                    a.target = '_blank';
                    a.download = filename; // nombre del archivo.
                    a.style.display = 'none';
                    document.body.appendChild(a);
                    a.click();
                    delete a;
                    }
                else{SubMsg.show();}
            }// fin xhr.onload  
            xhr.send();
           };   
         </script>
       </body>
    </html>

crear otro archivo con el nombre file.php

guardar como file.php

<?php
echo $_GET['tipo'];
if($_GET['datos_a_enviar']=="1"):
 switch($_GET['tipo']):
    case 'EXL':
        header("Content-type: application/vnd.ms-excel; name='excel'");
        header("Content-Disposition: filename=ficheroExcel.xls");
        header("Pragma: no-cache");
        header("Expires: 0");
    break;
    case 'PDF':
        header("Content-type:application/pdf");
        header("Content-Disposition:attachment;filename='downloaded.pdf'");
        readfile("my_archivo_pdf.pdf");
    break;  
endswitch; 
else:
    header("msj:No_Hay_datos"); 
endif;
sleep(2);
?>

0voto

blaDDI comentado

Listo lo he dejado en una respuesta, puedes revisarlo

0voto

Peter comentado

No se entienden tus multiples respuestas. Por favor explica que intentas.

Saludos.

0voto

blaDDI comentado

si queres probarlo, crea un archivo html o php; y luego crea un archivo php donde se procesa la respuesta el archivo php debe llevar por nombre file.php o el que quieras pero cambias esta linea var url = "file.php?datos_a_enviar=1&tipo=EXL"; y luego ejecutas el archivo html y listo

me explico ??

0voto

Peter comentado

El problema es que pusiste 4 respuestas por separado sin mencionar nada en cada una de ellas y en esta última pones esa descripción.

Lo ideal sería que publiques y expliques todo en una sola respuesta y no en 4. Para solucionar un tema hay que seleccionar una respuesta como correcta y si pones todo dividido en 4 respuestas, es imposible.

Saludos.

0voto

blaDDI comentado

De acuerdo, soy nuevo aquí

0voto

Peter comentado

No hay problema :) Si quieres copiar todo a una nueva respuesta y en ella explicar las cosas, borro todas estas y así se va a entender mejor tu respuesta.

Saludos.

0voto

blaDDI comentado

Listo ya lo dejé en una sola respuesta

0voto

Peter comentado

Ah, pero editaste la actual en donde estamos comentando. Copiala toda y publicala como una nueva respuesta abajo.

Saludos.

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta