7
votes

Écrivez sur la mémoire tampon de sortie PHP, puis téléchargez CSV à partir de tampon

J'ai besoin d'écrire un fichier CSV sur le tampon de sortie PHP, puis de télécharger ce fichier sur l'ordinateur du client après sa rédaction. (Je voulais simplement l'écrire sur le serveur et le télécharger, ce qui fonctionnait, mais je n'aurai pas accès en écriture sur les serveurs de production).

J'ai le script PHP suivant: P>

$basic_info = fopen("php://output", 'w');
$basic_header = array(HEADER_ITEMS_IN_HERE);

@fputcsv($basic_info, $basic_header);

while($user_row = $get_users_stmt->fetch(PDO::FETCH_ASSOC)) {
    @fputcsv($basic_info, $user_row);
}

@fclose($basic_info);

header('Content-Description: File Transfer');
header('Content-Type: application/csv');
header('Content-Disposition: attachment; filename=test.csv');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize("php://output"));
ob_clean();
flush();
readfile("php://output");


9 commentaires

Si vous écrivez sur le tampon de sortie, vous l'envoyez immédiatement au client. Auquel cas les en-têtes doivent être envoyés en premier.


ob_clean () est inutile à moins que vous ayez fait un ob_start () quelque part. Et considérez que ReadFile écrit à la sortie, mais vous lisez à partir de cette entrée. Sauf si vous avez fait un ob_start () avant tout ce code, votre FPTCSV () a écrit à la sortie et est allé sur le navigateur avant que l'un des codes d'en-tête ne soit exécuté.


Je peux nommer littéralement une méthode en PHP chaque fois que l'utilisation de "@" peut être justifiée. Ce n'est pas dans votre code, alors au lieu de planter @ sur chaque ligne, vous pouvez écrire votre code correctement au début?


@Webnetmobile non désériorize ()?


@Webnetmobile My Deveux est Mail () .


@Webnetmobile Je dispose en fait de la manipulation des erreurs dans le code mais je viens de le mettre là parce que je ne voulais pas inclure aucun de ce code


@DataSage pour que je puisse déplacer mes en-têtes avant d'écrire à la sortie. Mes en-têtes sont-ils corrects?


@Marcb Donc, si je déplace mes en-têtes au début, j'ai même besoin d'appels OB_CLEAN (), FUSH () ou LIFFILE ()?


non. Pourquoi tamponner quelque chose que vous allez simplement produire de toute façon? Vous ne produisez rien que les données CSV, la tampon est donc inutile.


3 Réponses :


11
votes

Vous faites un peu trop. Créez le script dans le seul but de sortir le CSV. Il suffit d'imprimer directement à l'écran. Ne vous inquiétez pas des en-têtes ou des tampons ou de la PHP: // la sortie ou quelque chose comme ça.

Une fois que vous avez confirmé que vous imprimez les données à l'écran de manière appropriée, ajoutez simplement ces en-têtes au début: < / p> xxx

... Confirmez que cela télécharge le fichier de manière appropriée. Ensuite, vous pouvez ajouter les autres en-têtes si vous le souhaitez (les en-têtes que j'ai inclus ci-dessus sont ceux que je me suis utilisé sans aucun cruft supplémentaire pour obtenir ce travail, les autres sont essentiellement pour l'efficacité et le contrôle de cache, dont certains peuvent déjà être traités de manière appropriée Par votre serveur, et peut être important ou non pour votre application particulière).

Si vous le souhaitez, utilisez la mémoire tampon de sortie avec ob_start () et ob_get_clean () < / Code> Pour obtenir le contenu de la sortie dans une chaîne que vous pouvez ensuite utiliser pour remplir longueur de contenu .


1 commentaires

merci de m'avoir aidé à différer les autres qui se plaingent de choses que je fais mal



0
votes

J'ai fait des modifications à votre code.

  • déplacé les en-têtes déplacés avant toute sortie, comme suggéré par doc de PHP ;

    N'oubliez pas que l'en-tête () doit être appelé avant que toute sortie réelle ne soit envoyé, soit par balises HTML normales, lignes vierges dans un fichier ou à partir de PHP

    • a retiré certains en-têtes qui n'ont pas fait beaucoup de changement;
    • a commenté une autre option d'écriture de l'en-tête CSV à l'aide des noms de sélection de colonne;
    • Maintenant la longueur de contenu fonctionne;
    • Il n'est pas nécessaire d'echo $ basic_info tel qu'il est déjà au tampon de sortie et nous l'avons redirigé à l'intérieur d'un fichier à travers des en-têtes;
    • Supprimé @ (opérateur de contrôle d'erreur PHP) car il peut causer des frais généraux, ne pas avoir de lien pour vous montrer maintenant mais que vous pourriez le trouver si vous recherchez. Vous devriez réfléchir à deux fois avant de faire de la silence des erreurs, la plupart des temps il devrait être fixé au lieu de silence.
      xxx

4 commentaires

Les en-têtes ne sont-ils pas déjà envoyés à l'en-tête («longueur de contenu:». $ Streamsize); Depuis que vous avez écrit à PHP: // sortie avant?


@dnfer Oui mais je ne peux connaître que la taille du fichier après la finition de l'écriture, ce qui fonctionne, le navigateur affiche la taille du fichier à télécharger Pop Up.


Je me rends compte. Mais je me demande une sorte que les "en-têtes déjà envoyés" avertissement ne se présentent pas.


J'avais reçu un message d'erreur "en-têtes déjà envoyé" lors de l'ajout de l'en-tête de longueur de contenu lorsque vous décrivez ci-dessus. Réussi à résoudre ceci en utilisant ob_start () avant la sortie CSV et ob_end_flush () après l'en-tête de longueur de contenu. En fonction de la fréquence de votre script pour traiter le téléchargement, le téléchargement commencera un peu plus tard que sans utiliser le tampon de sortie car l'ensemble du fichier sera préparé en premier, puis envoyé au client. L'UPSE est que l'utilisateur peut voir la taille du téléchargement et le navigateur peut mieux estimer le temps de téléchargement de téléchargement restant.



3
votes

Comme mentionné dans mes commentaires de la réponse d'Edson, je m'attendais à un avertissement "en-têtes déjà envoyé" à la dernière ligne de code: xxx

puisque la sortie est écrite avant que cet en-tête soit envoyé, mais Son exemple fonctionne bien.

Certaines enquêtes me conduisent à suivre les conclusions suivantes:

Au moment où vous utilisez un tampon de sortie (Wei, un utilisateur, ou le Par défaut PHP One), vous pouvez envoyer des en-têtes HTTP et le contenu de la manière dont vous vouloir. Vous savez que tout protocole nécessite d'envoyer des en-têtes avant le corps (Ainsi, le terme "en-tête"), mais lorsque vous utilisez une couche de tampon de sortie, PHP prendra en charge cela pour vous. Toute fonction PHP jouant avec la sortie les en-têtes (en-tête (), Setcookie (), Session_Start ()) utilisera en fait la Fonction interne sapi_header_op () qui se remplit juste dans les en-têtes amortir. Lorsque vous écrivez la sortie, utilisez Say Printf (), il écrit dans le tampon de sortie (en supposant un). lorsque le tampon de sortie doit être Envoyé, PHP commence à envoyer d'abord les en-têtes, puis le corps. Php prend soin de tout pour vous. si vous n'aimez pas ce comportement, vous n'ont pas d'autre choix que de désactiver toute couche tampon de sortie.

et

La taille par défaut du tampon PHP sous la plupart des configurations est 4096 octets (4 ko), ce qui signifie que les tampons PHP peuvent contenir des données jusqu'à 4 Ko. une fois que cela La limite est dépassée ou l'exécution de code PHP est terminée, contenu tampon est automatiquement envoyé à n'importe quelle analyse de dossier PHP est utilisée (CGI, mod_php, fastcgi). La mémoire tampon de sortie est toujours éteinte dans PHP-CLI.

Le code d'Edson fonctionne car le tampon de sortie ne s'est pas produit automatiquement, car il ne dépasse pas la taille de la mémoire tampon (et le script n'est pas terminé évidemment avant l'envoi de la dernière en-tête). < P> Dès que les données du tampon de sortie dépassent la taille du tampon, l'avertissement sera soulevé. Ou dans son exemple, lorsque les données de xxx

sont trop grandes.

Pour éviter cela, vous devez gérer la mise en mémoire tampon de sortie avec l'OB_START ( ) et ob_end_flush (); Comme ci-dessous: xxx

Vous êtes toujours lié à d'autres limites, cependant.


0 commentaires