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?
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.
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'];
}
?>
La meilleure façon d'y parvenir est d'utiliser un URI de données comme suit:
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.
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
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.
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.