2010-07-27 37 views
23

Estoy intentando realizar una tarea bastante simple para mi sitio web, pero no estoy seguro de cómo hacerlo. Quiero que el usuario vea una tabla, luego haga clic en botón, en cuyo punto el usuario puede guardar el contenido de esa tabla como un archivo csv. Esta solicitud a veces puede ser bastante complicada, así que genero una página de progreso para alertar al usuario.Descargar el archivo CSV usando "AJAX"

Tengo la mayoría de las cosas resueltas excepto en realidad generar el archivo csv (utilizo jQuery y PHP)

el código de ejecución en jQuery clic:.

hmis_query_csv_export: function(query_name) { 
    $.uiLock('<p>Query Loading.</p><img src="/images/loading.gif" />') 
    $.get({ 
     url: '/php_scripts/utils/csv_export.php', 
     data: {query_name: query_name}, 
     success: function(data) { 
      $.uiUnlock(); 
     } 
    });} 

el PHP relevante:

header("Content-type: text/x-csv"); 
header("Content-Disposition: attachment; filename=search_results.csv"); 
// 
//Generate csv 
// 
echo $csvOutput 
exit();

Lo que esto hace es envía el texto que el archivo PHP, pero es que no genera una descarga. ¿Qué estoy haciendo mal?

Respuesta

36

Si está forzando una descarga, puede redirigir la página actual al enlace de descarga. Como el enlace generará un diálogo de descarga, la página actual (y su estado) se mantendrá en su lugar.

enfoque básico:

$('a#query_name').click(function(){ 
    $('#wait-animation').show(); 
    document.location.href = '/php_scripts/utils/csv_export.php?query_name='+query_name; 
    $('#wait-animation').hide(); 
}); 

más complicado:

$('a#query_name').click(function(){ 
    MyTimestamp = new Date().getTime(); // Meant to be global var 
    $('#wait-animation').show(); 
    $.get('/php_scripts/utils/csv_export.php','timestamp='+MyTimestamp+'&query_name='query_name,function(){ 
     document.location.href = '/php_scripts/utils/csv_export.php?timestamp='+MyTimestamp+'&query_name='+query_name; 
     $('#wait-animation').hide(); 
    }); 
}); 

En script PHP:

@header("Last-Modified: " . @gmdate("D, d M Y H:i:s",$_GET['timestamp']) . " GMT"); 
@header("Content-type: text/x-csv"); 
// If the file is NOT requested via AJAX, force-download 
if(!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') { 
    header("Content-Disposition: attachment; filename=search_results.csv"); 
} 
// 
//Generate csv 
// 
echo $csvOutput 
exit(); 

La URL para ambas solicitudes debe ser el mismo para engañar al navegador que no comience una nueva descarga al document.location.href, pero para guardar la copia en el caché. No estoy totalmente seguro, pero parece bastante prometedor.

+0

Muchas gracias por los consejos. Puedo hacer que esto funcione muy fácilmente, pero me pregunto cómo podría usar esto mientras sigo implementando un bloqueo de pantalla y una animación de progreso. Me preocupa que los usuarios se confundan, ya que dependiendo de la consulta este proceso puede tomar hasta 3 o 4 minutos. –

+1

Si va a llevar tanto tiempo crear el archivo CSV, puede necesitar cambiar ligeramente las direcciones y en su lugar usar AJAX para iniciar un script PHP que crea un archivo de disco temporal; una vez que el script PHP retorna (con un nombre de archivo) la función de devolución de llamada AJAX puede hacer la redirección como muestra Ast Derek. – godheadatomic

+0

Por lo tanto, su edición parece que debería funcionar, pero parece que las declaraciones se ejecutan de forma asíncrona. La tercera instrucción no espera a que finalice la descarga, por lo que la pantalla de espera aparece y luego desaparece instantáneamente. ¿Hay alguna forma de forzarlo a ejecutar sincrónicamente? –

1

No creo que pueda realizar la descarga del navegador utilizando una solicitud de AJAX/JS. Intente utilizar un iframe oculto que navegue a la página que genera el CSV

0

Bueno, el objetivo de utilizar AJAX es evitar una recarga visible de la página. Si desea una descarga, quiere lo contrario, una nueva solicitud del navegador. Yo diría que simplemente crea un botón simple que apunta a tu página php.

+0

Ok, si hago eso, ¿cómo integraría una animación de carga y un bloqueo de UI en ella? ¿No sería esa una razón para usar AJAX? Además, soy bastante nuevo en esto y no estoy seguro de la mejor manera de hacerlo al integrar el parámetro GET en la solicitud de la página. –

+0

@The_Denominator utiliza un iframe –

0

Para hacer eco y ampliar lo que otros han dicho, realmente no se puede enviar el archivo con AJAX. Una de las razones para esto es (y alguien me corrige si me equivoco con esto, por favor) que la página en la que se encuentra actualmente ya ha enviado sus encabezados de contenido; no puede volver a enviarlos a la misma ventana, incluso con una solicitud AJAX (que es lo que intenta hacer su archivo PHP).

Lo que he hecho antes en proyectos es simplemente proporcionar un enlace (con target = "_ blank" o redirección javascript) a una página de descarga de PHP por separado. Si está utilizando Apache, consulte también mod_xsendfile.

5

EDIT Acabo de probar esto con un archivo de 10 MB y parece que val() es demasiado lento para insertar los datos. Hurrumph.


Bien, así que le di uno a otro. ¡Esto puede o no ser completamente loco!La idea es hacer una solicitud de AJAX para crear los datos, luego usar la devolución de llamada para insertar los datos en un formulario oculto en la página actual que tiene una acción de una tercera página de "descarga"; después de la inserción, el formulario se envía automáticamente, la página de descarga envía encabezados y se hace eco de la POST, y et voila, descarga.

Mientras tanto, en la página original tiene una indicación de que el archivo se está preparando, y cuando termina el indicador se actualiza.

NOTA: este código de prueba no se ha probado exhaustivamente, y no tiene comprobaciones de seguridad real (o ninguna) puesta en marcha. Lo probé con un archivo CSV de 1.5MB que tenía sobre y era bastante ágil.

index.html

<a id="downloadlink" href="#">Click Me</a> 
<div id="wait"></div> 
<form id="hiddenform" method="POST" action="download.php"> 
    <input type="hidden" id="filedata" name="data" value=""> 
</form> 

Test.js

$(document).ready(function(){ 
    $("#downloadlink").click(function(){  // click the link to download 
     lock();        // start indicator 
     $.get("create.php",function(filedata){ // AJAX call returns with CSV file data 
      $("#filedata").val(filedata);  // insert into the hidden form 
      unlock();       // update indicator 
      $("#hiddenform").submit();   // submit the form data to the download page 
     }); 
    }); 

    function lock(){ 
     $("#wait").text("Creating File..."); 
    } 

    function unlock(){ 
     $("#wait").text("Done"); 
    } 
}); 

create.php

<?php 
//create $data 
print $data; 
?> 

download.php

<?php 
header("Pragma: public"); 
header("Expires: 0"); 
header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); 
header("Content-Type: text/x-csv"); 
header("Content-Disposition: attachment;filename=\"search_results.csv\""); 

if($_POST['data']){ 
    print $_POST['data']; 
} 
?> 
3

La mejor manera de lograr esto es utilizar un URI de datos de la siguiente manera:

  1. Haga la llamada AJAX al servidor con normalidad
  2. Generar el archivo CSV en el lado del servidor
  3. Devuelve los datos (ya sea al descubierto o dentro de una estructura JSON)
  4. Cree un URI de datos en Javascript utilizando los datos devueltos
  5. Conjunto window.location.href a la URI de datos

Ver este enlace para obtener instrucciones (párrafo # 3, específicamente): http://en.wikipedia.org/wiki/Data_URI_scheme

esta manera, usted no necesita guardar los archivos en el servidor, y tampoco es necesario utilizar iframes o elementos de formulario ocultos o cualquiera de estos hacks.

Cuestiones relacionadas