web-dev-qa-db-fra.com

Téléchargez le fichier CSV en utilisant "AJAX"

J'essaie d'accomplir une tâche assez simple pour mon site Web, mais je ne sais pas exactement comment procéder. Je souhaite que l'utilisateur consulte un tableau, puis clique sur un bouton, auquel cas l'utilisateur peut enregistrer le contenu de cette table sous forme de fichier csv. Cette requête peut parfois être assez compliquée donc je génère une page de progression pour alerter l'utilisateur.

J'ai compris la plupart des choses, sauf la génération du fichier csv. (J'utilise jQuery et PHP)

le code jQuery s'exécute au 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();
        }
    });}

le PHP pertinent:

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

Ce que cela fait, c'est d'envoyer le texte sous forme de fichier PHP, mais il ne génère pas de téléchargement. Que fais-je de mal?

25
The_Denominater

Si vous forcez un téléchargement, vous pouvez rediriger la page actuelle vers le lien de téléchargement. Puisque le lien va générer une boîte de dialogue de téléchargement, la page actuelle (et son état) sera maintenue en place.

Approche de base:

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

Plus compliqué:

$('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();
    });
});

À 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();

L'URL des deux requêtes doit être la même pour inciter le navigateur à ne pas démarrer un nouveau téléchargement à document.location.href, mais pour enregistrer la copie dans le cache. Je n'en suis pas totalement sûr, mais cela semble assez prometteur.

43
Ast Derek

EDIT Je viens d'essayer ceci avec un fichier de 10 Mo et il semble que val () soit trop lent pour insérer les données. Hurrumph.


D'accord, j'ai donc donné un autre coup à celui-ci. Cela peut ou peut ne pas être complètement fou! L'idée est de faire une demande AJAX pour créer les données, puis d'utiliser le rappel pour insérer les données dans un formulaire caché sur la page courante qui a une action d'une troisième page de "téléchargement"; après l'insertion, le formulaire est automatiquement envoyé, la page de téléchargement envoie les en-têtes et fait écho au POST, et le tour est joué, téléchargez.

Pendant tout ce temps, sur la page d'origine, vous avez une indication que le fichier est en cours de préparation et quand il se termine, l'indicateur est mis à jour.

REMARQUE: ce code de test n'est pas testé de manière approfondie et n'a aucun véritable contrôle de sécurité (ou aucun) mis en place. Je l'ai testé avec un fichier CSV de 1,5 Mo sur lequel je m'attardais et c'était assez accrocheur.

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'];
}
?>
5
godheadatomic

La meilleure façon d'y parvenir est d'utiliser un URI de données comme suit:

  1. Faire l'appel AJAX au serveur comme d'habitude
  2. Générez le CSV côté serveur
  3. Renvoyer les données (nues ou à l'intérieur d'une structure JSON)
  4. Créer un URI de données en Javascript en utilisant les données retournées
  5. Définissez window.location.href sur l'URI de données

Voir ce lien pour les instructions (paragraphe # 3 en particulier): http://en.wikipedia.org/wiki/Data_URI_scheme

De cette façon, vous n'avez pas besoin d'enregistrer de fichiers sur le serveur, et vous n'avez également pas besoin d'utiliser des iframes ou des éléments de formulaire cachés ou de tels hacks.

3
Aquarelle

Je ne pense pas que vous puissiez effectuer le téléchargement du navigateur à l'aide d'une demande AJAX/JS. Essayez d'utiliser un iframe caché qui accède à la page qui génère le CSV

1
Bob Fincheimer

Pour faire écho et développer ce que les autres ont dit, vous ne pouvez pas vraiment envoyer le fichier à l'aide d'AJAX. L'une des raisons à cela est (et quelqu'un me corrige si je me trompe, s'il vous plaît) que la page sur laquelle vous êtes actuellement a déjà envoyé ses en-têtes de contenu; vous ne pouvez pas les envoyer à nouveau dans la même fenêtre, même avec une demande AJAX (ce que votre fichier PHP tente de faire)).

Ce que j'ai fait auparavant dans les projets, c'est simplement de fournir un lien (avec target = "_ blank" ou redirection javascript) vers un téléchargement séparé PHP page. Si vous utilisez Apache, consultez mod_xsendfile également.

0
godheadatomic

Eh bien le point d'utiliser AJAX est d'éviter un rechargement visible de la page. Si vous voulez un téléchargement, vous voulez l'inverse, - une toute nouvelle demande du navigateur. Je dirais, créez simplement un simple bouton pointant vers votre page php.

0
Nicolas78