web-dev-qa-db-fra.com

La fonctionnalité onprogress peut-elle être ajoutée à jQuery.ajax () en utilisant xhrFields?

Comme suggéré ici: https://Gist.github.com/HenrikJoreteg/2502497 , j'essaie d'ajouter une fonctionnalité de progression à mon téléchargement de fichier jQuery.ajax(). Le téléchargement fonctionne correctement et l'événement onprogress se déclenche, mais pas comme je m'y attendais - au lieu de se répéter à intervalles réguliers, il ne se déclenche qu'une seule fois, lorsque le téléchargement est terminé. Existe-t-il un moyen de spécifier la fréquence des rafraîchissements en cours? Ou est-ce que j'essaye de faire quelque chose qui ne peut pas être fait? Voici mon code:

$.ajax(
{
    async: true,
    contentType: file.type,
    data: file,
    dataType: 'xml',
    processData: false,
    success: function(xml)
    {
        // Do stuff with the returned xml
    },
    type: 'post',
    url: '/fileuploader/' + file.name,
    xhrFields:
    {
        onprogress: function(progress)
        {
            var percentage = Math.floor((progress.total / progress.totalSize) * 100);
            console.log('progress', percentage);
            if (percentage === 100)
            {
                console.log('DONE!');
            }
        }
    }
});
25
dcorsello

Réponse courte:
Non, vous ne pouvez pas faire ce que vous voulez en utilisant xhrFields.

Réponse longue:

Il existe deux événements de progression dans un objet XmlHttpRequest:

  • La progression de la réponse (XmlHttpRequest.onprogress)
    C'est lorsque le navigateur télécharge les données depuis le serveur.

  • La progression de la demande (XmlHttpRequest.upload.onprogress)
    C'est lorsque le navigateur envoie les données au serveur (y compris POST, cookies et fichiers)

Dans votre code, vous utilisez l'événement de progression de la réponse, mais ce dont vous avez besoin est l'événement de progression de la demande. Voici comment procéder:

$.ajax({
    async: true,
    contentType: file.type,
    data: file,
    dataType: 'xml',
    processData: false,
    success: function(xml){
        // Do stuff with the returned xml
    },
    type: 'post',
    url: '/fileuploader/' + file.name,
    xhr: function(){
        // get the native XmlHttpRequest object
        var xhr = $.ajaxSettings.xhr() ;
        // set the onprogress event handler
        xhr.upload.onprogress = function(evt){ console.log('progress', evt.loaded/evt.total*100) } ;
        // set the onload event handler
        xhr.upload.onload = function(){ console.log('DONE!') } ;
        // return the customized object
        return xhr ;
    }
});

Le paramètre d'option xhr doit être une fonction qui renvoie un objet XmlHttpRequest natif pour que jQuery l'utilise.

65
GetFree

Vous devez ajouter un gestionnaire d'événements à la demande elle-même avant qu'elle ne soit envoyée. jQuery.ajax permet cela via la propriété 'beforeSend' http://api.jquery.com/jQuery.ajax/

par exemple: http://www.dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5/

* EDIT * Assurez-vous de regarder le deuxième exemple de code de cet exemple de lien. Je crois que le premier est obsolète avec les versions modernes de jQuery.

$.ajax({
  xhr: function()
  {
    var xhr = new window.XMLHttpRequest();
    //Upload progress
    xhr.upload.addEventListener("progress", function(evt){
      if (evt.lengthComputable) {
        var percentComplete = evt.loaded / evt.total;
        //Do something with upload progress
        console.log(percentComplete);
      }
    }, false);
    //Download progress
    xhr.addEventListener("progress", function(evt){
      if (evt.lengthComputable) {
        var percentComplete = evt.loaded / evt.total;
        //Do something with download progress
        console.log(percentComplete);
      }
    }, false);
    return xhr;
  },
  type: 'POST',
  url: "/",
  data: {},
  success: function(data){
    //Do something success-ish
  }
});
3
CooCooCaCha

C'est un peu une autre réponse ailleurs sur Stack Overflow, mais je pense que cela répond à votre question. Je l'ai adapté à mes propres besoins de pompage des données "en streaming" du serveur vers un div défilable (que je peux ensuite forcer à toujours faire défiler vers le bas, montrant ainsi la progression dans le temps et donc n'attendant pas la fin de l'ensemble des enregistrements) .

Le code côté client ci-dessous ajoute le contenu résultant à un div prédéfini avec l'ID "scrollable_area" (qui peut ensuite être défilé) ...

<div style="position:absolute; left:5px; right:5px; top:5px; height:35px;">
    <label for="auto_scroll">Auto Scroll</label> <input type="checkbox" id="auto_scroll" checked>
</div>
<div id="scrollable_area" style="position:absolute; overflow:auto; left:5px; right:5px; top:45px; bottom:5px;"></div>
<script type="text/javascript">
    var last_response_len = false;
    var auto_scroll = null;
    var scrollable_area = null;
    $().ready(function() {
        auto_scroll = document.getElementById("auto_scroll");
        scrollable_area = document.getElementById("scrollable_area");
        $.ajax("your_api_call.php", {
            xhrFields: {
                onprogress: function(e) {
                    var this_response, response = e.currentTarget.response;
                    if(last_response_len === false) {
                        this_response = response;
                        last_response_len = response.length;
                    } else {
                        this_response = response.substring(last_response_len);
                        last_response_len = response.length;
                    }
                    scrollable_area.innerHTML += this_response;
                    if(auto_scroll.checked) {
                        scrollable_area.scrollTop = scrollable_area.clientHeight + scrollable_area.scrollHeight + 500;
                    }
                }
            }
        })
        .done(function(data) {
            console.log("Completed response");
        })
        .fail(function(data) {
            console.log("Error: ", data);
        });
        console.log("your_api_call.php Request Sent!");
    });
</script>

L'appel de fichier côté serveur "your_api_call.php" devrait alors vider sa sortie (par ligne de données afin de voir la progression dans le temps) qui peut ensuite être affichée immédiatement dans la div "scrollable_area" ci-dessus. ..

// Do Db loop
        while ($record = $recordset->fetch(PDO::FETCH_ASSOC)) {
            set_time_limit(10); // Don't timeout on large data sets seeing as this is a big task that we are wanting to watch progress!
            echo 'Do what you gotta do... ' . $record["register_id"] . '<br>';
            flush(); // Push to the client / ajax
            ob_flush(); // As above
        }

Réponse courte ... OUI. J'espère que cela t'aides :-)

1
Andrew Foster