583 votes

Détecter lorsque le navigateur reçoit le dossier de téléchargement

J'ai une page qui permet à l'utilisateur de télécharger un générées dynamiquement fichier. Il faut beaucoup de temps pour générer, donc j'aimerais vous montrer une "attente" de l'indicateur. Le problème est, je ne peux pas comprendre comment détecter si le navigateur a reçu le fichier, donc je ne peux cacher l'indicateur.

Je suis en train de faire la demande sous une forme cachée, qui publie sur le serveur, et les objectifs d'une iframe masqué par ses résultats. C'est le cas je ne remplacez pas l'ensemble de la fenêtre du navigateur avec le résultat. J'écoute pour une "charge" d'un événement sur l'iframe, dans l'espoir qu'il va se déclencher quand le téléchargement est terminé.

- Je retourner un "Content-Disposition: attachment" en-tête avec le fichier, ce qui provoque le navigateur pour afficher le dialogue "Enregistrer". Mais le navigateur ne prend pas le feu à une "charge" de l'événement dans l'iframe.

Une approche que j'ai essayé en utilisant un multi-partie de la réponse. Afin d'envoyer un fichier HTML vide, ainsi que le fichier téléchargeable. Par exemple:

Content-type: multipart/x-mixed-replace;boundary="abcde"

--abcde
Content-type: text/html

--abcde
Content-type: application/vnd.fdf
Content-Disposition: attachment; filename=foo.fdf

file-content
--abcde

Cela fonctionne dans Firefox; il reçoit le vide d'un fichier HTML, les feux de la "charge" de l'événement, puis affiche le dialogue "Enregistrer" pour télécharger ce fichier. Mais ceci ne fonctionne pas sur IE et Safari, c'est à dire les feux de la "charge" de l'événement, mais ne pas télécharger le fichier, et Safari télécharge le fichier (avec le mauvais nom et le contenu-type), et de ne pas le feu à la "charge" de l'événement.

Une autre approche pourrait être de faire un appel à lancer la création du fichier, puis interroger le serveur jusqu'à ce qu'il est prêt, puis de télécharger le fichier créé. Mais je préfère éviter de créer des fichiers temporaires sur le serveur.

Quelqu'un aurait-il une meilleure idée?

507voto

bulltorious Points 2664

Une solution possible utilise le JavaScript sur le client.

Le client algorithme:

  1. La génération aléatoire d'jeton unique.
  2. Soumettre la demande de téléchargement, et inclure le jeton dans un GET/POST champ.
  3. Montrer le voyant "attente".
  4. Démarrer une minuterie, et chaque seconde ou recherchez un cookie nommé "fileDownloadToken" (ou ce que vous décidez).
  5. Si le cookie existe, et sa valeur correspond au jeton, de masquer le voyant "attente".

Le serveur de l'algorithme:

  1. Recherchez les GET/POST champ dans la requête.
  2. Si elle a une valeur non vide, déposer un cookie (par exemple, "fileDownloadToken"), et définissez sa valeur à la valeur de jeton.

Client source code (JavaScript):

  function getCookie( name ) {
    var parts = document.cookie.split(name + "=");
    if (parts.length == 2) return parts.pop().split(";").shift();
  }

  function expireCookie( cName ) {
    document.cookie = 
      encodeURIComponent( cName ) +
      "=deleted; expires=" +
      new Date( 0 ).toUTCString();
  }

  function setCursor( docStyle, buttonStyle ) {
    document.getElementById( "doc" ).style.cursor = docStyle;
    document.getElementById( "button-id" ).style.cursor = buttonStyle;
  }

  function setFormToken() {
    var downloadToken = new Date().getTime();
    document.getElementById( "downloadToken" ).value = downloadToken;
    return downloadToken;
  }

  var downloadTimer;
  var attempts = 30;

  // Prevents double-submits by waiting for a cookie from the server.
  function blockResubmit() {
    var downloadToken = setFormToken();
    setCursor( "wait", "wait" );

    downloadTimer = window.setInterval( function() {
      var token = getCookie( "downloadToken" );

      if( (token == downloadToken) || (attempts == 0) ) {
        unblockSubmit();
      }

      attempts--;
    }, 1000 );
  }

  function unblockSubmit() {
    setCursor( "auto", "pointer" );
    window.clearInterval( downloadTimer );
    expireCookie( "downloadToken" );
  }

Exemple de code serveur (PHP):

    $TOKEN = "downloadToken";

    // Sets a cookie so that when the download begins the browser can
    // unblock the submit button (thus helping to prevent multiple clicks).
    // The false parameter allows the cookie to be exposed to JavaScript.
    $this->setCookieToken( $TOKEN, $_GET[ $TOKEN ], false );

    $result = $this->sendFile();

Où:

  public function setCookieToken(
    $cookieName, $cookieValue, $httpOnly = true, $secure = false ) {

    // See: http://stackoverflow.com/a/1459794/59087
    // See: http://shiflett.org/blog/2006/mar/server-name-versus-http-host
    // See: http://stackoverflow.com/a/3290474/59087
    setcookie(
      $cookieName,
      $cookieValue,
      2147483647,            // expires January 1, 2038
      "/",                   // your path
      $_SERVER["HTTP_HOST"], // your domain
      $secure,               // Use true over HTTPS
      $httpOnly              // Set true for $AUTH_COOKIE_NAME
    );
  }

29voto

birdman Points 91

Un exemple très simple (et boiteux) une solution est d'utiliser l' window.onblur() événement pour fermer le dialogue de chargement. Bien sûr, si elle est trop longue et que l'utilisateur décide de faire autre chose (comme la lecture des emails), le chargement de la boîte de dialogue se ferme.

21voto

bytepirate Points 201

vieux thread, je sais...

mais ceux qui sont ici par google pourrait être intéressé à ma solution. c'est très simple, mais fiable. et il rend possible l'affichage de réels progrès messages (et peut être facilement branché dans les processus existants):

le script qui traite (mon problème était le suivant: la récupération de fichiers via http, et de les livrer en tant que zip) enregistre l'état de la session.

le statut est interrogé et affiché à chaque seconde. c'est tout (ok, ce n'est pas. vous devez prendre soin de beaucoup de détails [par exemple les téléchargements simultanés], mais c'est un bon endroit pour commencer ;-)).

le downloadpage:

    <a href="download.php?id=1" class="download">DOWNLOAD 1</a>
    <a href="download.php?id=2" class="download">DOWNLOAD 2</a>
    ...
    <div id="wait">
    Please wait...
    <div id="statusmessage"></div>
    </div>
    <script>
//this is jquery
    $('a.download').each(function()
       {
        $(this).click(
             function(){
               $('#statusmessage').html('prepare loading...');
               $('#wait').show();
               setTimeout('getstatus()', 1000);
             }
          );
        });
    });
    function getstatus(){
      $.ajax({
          url: "/getstatus.php",
          type: "POST",
          dataType: 'json',
          success: function(data) {
            $('#statusmessage').html(data.message);
            if(data.status=="pending")
              setTimeout('getstatus()', 1000);
            else
              $('#wait').hide();
          }
      });
    }
    </script>

getstatus.php

<?php
session_start();
echo json_encode($_SESSION['downloadstatus']);
?>

download.php

    <?php
    session_start();
    $processing=true;
    while($processing){
      $_SESSION['downloadstatus']=array("status"=>"pending","message"=>"Processing".$someinfo);
      session_write_close();
      $processing=do_what_has_2Bdone();
      session_start();
    }
      $_SESSION['downloadstatus']=array("status"=>"finished","message"=>"Done");
//and spit the generated file to the browser
    ?>

12voto

Elmer Points 2377

j'utilise la suite pour télécharger des gouttes et de révoquer l'objet de l'url après le téléchargement. il fonctionne dans chrome et firefox!

function download(blob){
    var url = URL.createObjectURL(blob);
    console.log('create ' + url);

    window.addEventListener('focus', window_focus, false);
    function window_focus(){
        window.removeEventListener('focus', window_focus, false);                   
        URL.revokeObjectURL(url);
        console.log('revoke ' + url);
    }
    location.href = url;
}

après le téléchargement de fichier boîte de dialogue est fermée, la fenêtre elle obtient le focus de sorte que l'objet événement est déclenché.

6voto

Andrew Points 97

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