106 votes

fermer une connexion tôt

J'essaie de faire un appel AJAX (via JQuery) qui lancera un processus assez long. J'aimerais que le script envoie simplement une réponse indiquant que le processus a démarré, mais JQuery ne renverra pas la réponse tant que le script PHP ne sera pas exécuté.

J'ai essayé cela avec un en-tête "close" (ci-dessous), et aussi avec la mise en mémoire tampon de sortie; ni semble fonctionner. Des suppositions? ou est-ce quelque chose que je dois faire dans JQuery?

 <?php

echo( "We'll email you as soon as this is done." );

header( "Connection: Close" );

// do some stuff that will take a while

mail( 'dude@thatplace.com', "okay I'm done", 'Yup, all done.' );

?>
 

95voto

Joeri Sebrechts Points 7483

Le PHP suivant de la page de manuel (incl. utilisateur-notes) suggère plusieurs instructions sur la façon de fermer la connexion TCP vers le navigateur, sans se terminant le script PHP:

Soi-disant, il faut un peu plus que l'envoi d'un proche de l'en-tête.


OP ensuite, confirme: oui, cela a fait le tour: pointant vers l'utilisateur note #71172 (Nov 2006) copié ici:

La fermeture de la utilisateurs du navigateur de connexion tout en gardant votre script php en cours d'exécution a été un problème depuis [PHP] 4.1, lorsque le comportement de l' register_shutdown_function() a été modifié de sorte qu'il ne serait pas automatiquement la fermeture de la connexion.

sts au mail dot xubion dot hu Posté le solution originale:

<?php
header("Connection: close");
ob_start();
phpinfo();
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush();
flush();
sleep(13);
error_log("do something in the background");
?>

Qui fonctionne très bien jusqu'à ce que vous substituer phpinfo() pour echo('text I want user to see'); , auquel cas les en-têtes ne sont jamais envoyés!

La solution est explicitement désactiver le tampon de sortie et effacer la mémoire tampon avant l'envoi de vos informations d'en-tête. Exemple:

<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(true); // just to be safe
ob_start();
echo('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Strange behaviour, will not work
flush(); // Unless both are called !
// Do processing here 
sleep(30);
echo('Text user will never see');
?>

Je viens de passer 3 heures à essayer de comprendre celui-ci, espérons que cela aide quelqu'un :)

Testé dans:

  • IE 7.5730.11
  • Mozilla Firefox 1.81

Plus tard, en juillet 2010 dans une réponse similaire de l'Arctique Feu ensuite lié les deux autres de l'utilisateur-notes qui ont été suivis à celui ci-dessus:

59voto

Timbo White Points 530

Il est nécessaire d'envoyer ces 2 têtes:

Connection: close
Content-Length: n (n = size of output in bytes )

Puisque vous avez besoin de connaître la taille de votre sortie, vous aurez besoin de tampon de sortie, puis le rincer à l'navigateur:

// buffer all upcoming output
ob_start();
echo "We'll email you as soon as this is done.";

// get the size of the output
$size = ob_get_length();

// send headers to tell the browser to close the connection
header("Content-Length: $size");
header('Connection: close');

// flush all output
ob_end_flush();
ob_flush();
flush();

// if you're using sessions, this prevents subsequent requests
// from hanging while the background process executes
if (session_id()) session_write_close();

/******** background process starts here ********/

Aussi, si votre serveur web est automatique à l'aide de la compression gzip sur la sortie (ie. Apache avec le mod_deflate), cela ne fonctionne pas, car la taille de la sortie est modifiée, et la Longueur du Contenu n'est plus d'actualité. Désactiver la compression gzip le script particulier.

Pour plus de détails, visitez http://www.zulius.com/how-to/close-browser-connection-continue-execution

21voto

Jorrit Schippers Points 913

Vous pouvez utiliser Fast-CGI avec PHP-FPM pour utiliser l' fastcgi_end_request() fonction. De cette façon, vous pouvez continuer à effectuer un traitement bien que la réponse a déjà été envoyée au client.

Vous trouverez cela dans le manuel PHP ici: FastCGI Process Manager (FPM); Mais que spécifiquement fonction n'est pas documentée dans le manuel. Voici l'extrait de l' PHP-FPM: PHP FastCGI Process Manager Wiki:


fastcgi_finish_request()

Champ d'application: fonction php Catégorie: Optimisation

Cette fonctionnalité vous permet d'accélérer la mise en œuvre de certaines requêtes php. L'accélération est possible quand il y a des actions dans le processus de l'exécution de script qui n'affectent pas de réponse du serveur. Par exemple, l'enregistrement de la session dans memcached peut se produire après que la page a été constitué et transmis à un serveur web. fastcgi_finish_request() est une fonction php, qui s'arrête à la sortie de la réponse. Serveur Web commence tout de suite à la réponse du transfert "lentement et tristement" pour le client, et php dans le même temps, on peut faire beaucoup de choses utiles dans le contexte d'une requête, comme l'enregistrement de la session, la conversion de la vidéo téléchargée, la manutention de tous types de statistiques, etc.

fastcgi_finish_request() peut invoquer l'exécution de l'arrêt de la fonction.

17voto

diyism Points 1191

Version complète:

 ignore_user_abort(true);//avoid apache to kill the php running
ob_start();//start buffer output

echo "show something to user";
session_write_close();//close session file on server side to avoid blocking other requests

header("Content-Encoding: none");//send header to avoid the browser side to take content as gzip format
header("Content-Length: ".ob_get_length());//send length header
header("Connection: close");//or redirect to some url: header('Location: http://www.google.com');
ob_end_flush();flush();//really send content, can't change the order:1.ob buffer to normal buffer, 2.normal buffer to output

//continue do something on server side
ob_start();
sleep(5);//the user won't wait for the 5 seconds
echo 'for diyism';//user can't see this
file_put_contents('/tmp/process.log', ob_get_contents());
ob_end_clean();
 

4voto

Liam Points 5214

En supposant que vous avez un serveur Linux et un accès root, essayez ceci. C'est la solution la plus simple que j'ai trouvé.

Créer un nouveau répertoire pour les fichiers suivants et donner les autorisations complètes. (On peut la rendre plus sûre, plus tard.)

mkdir test
chmod -R 777 test
cd test

Mettez ceci dans un fichier appelé bgping.

echo starting bgping
ping -c 15 www.google.com > dump.txt &
echo ending bgping

Remarque l' &. La commande ping sera exécuté en arrière-plan pendant que le processus actuel se déplace sur la commande echo. Il va de ping www.google.com 15 fois, ce qui prend environ 15 secondes.

Le rendre exécutable.

chmod 777 bgping

Mettez ceci dans un fichier appelé bgtest.php.

<?php

echo "start bgtest.php\n";
exec('./bgping', $output, $result)."\n";
echo "output:".print_r($output,true)."\n";
echo "result:".print_r($result,true)."\n";
echo "end bgtest.php\n";

?>

Lorsque vous demandez bgtest.php dans votre navigateur, vous devriez obtenir la réponse suivante rapidement, sans attendre sur 15 secondes pour que la commande ping pour terminer.

start bgtest.php
output:Array
(
    [0] => starting bgping
    [1] => ending bgping
)

result:0
end bgtest.php

La commande ping devrait maintenant être en cours d'exécution sur le serveur. Au lieu de la commande ping, vous pouvez exécuter un script PHP:

php -n -f largejob.php > dump.txt &

Espérons que cette aide!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X