45 votes

Comment utiliser un contenu déflaté / gzippé avec une fonction XHR onProgress?

J'ai vu un tas de questions similaires à cette demande avant, mais je n'ai pas trouvé un qui décrit mon problème actuel exactement, donc voilà:

J'ai une page qui charge un grand (entre 0,5 et 10 MO) document JSON via AJAX, de sorte que le code côté client peut la traiter. Une fois le fichier chargé, je n'ai pas de problèmes que je ne m'attends pas. Cependant, il faut un temps de téléchargement, j'ai donc essayé de tirer parti de la XHR Progrès de l'API pour rendre une barre de progression pour indiquer à l'utilisateur que le document est en cours de chargement. Cela a bien fonctionné.

Puis, dans un effort pour accélérer les choses, j'ai essayé de la compression de la sortie sur le côté serveur via gzip et deflate. Cela a fonctionné aussi, avec d'énormes gains, cependant, ma barre de progression de l'arrêt de travail.

J'ai regardé dans le problème pendant un certain temps et a constaté que si un bon Content-Length d'en-tête n'est pas envoyé avec la demande AJAX ressources, l' onProgress gestionnaire d'événement ne peut pas fonctionner comme prévu car il ne sait pas combien de temps le téléchargement il est. Lorsque cela se produit, une propriété appelée lengthComputable est définie à l' false sur l'objet de l'événement.

Ce sens faite, alors, j'ai essayé de réglage de l'en-tête de manière explicite à la fois décompressé et par la compression de la longueur de la sortie. Je peux vérifier que l'en-tête est envoyé, et j'ai pu vérifier que mon navigateur sait comment décompresser le contenu. Mais l' onProgress gestionnaire de rapports toujours lengthComputable = false.

Donc ma question est: est-il un moyen de gzippé/dégonflé contenu avec l'AJAX Progrès de l'API? Et si oui, que fais-je tort?


C'est la façon dont la ressource apparaît dans le Chrome Réseau de bord, montrant que la compression de travail:

network panel

Ce sont pertinentes demande des en-têtes, montrant que la demande est AJAX et qu' Accept-Encoding correctement:

GET /dashboard/reports/ajax/load HTTP/1.1
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: application/json, text/javascript, */*; q=0.01
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.99 Safari/537.22
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

Ces sont les réponse des en-têtes, montrant que l' Content-Length et Content-Type sont définies correctement:

HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Encoding: deflate
Content-Type: application/json
Date: Tue, 26 Feb 2013 18:59:07 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
P3P: CP="CAO PSA OUR"
Pragma: no-cache
Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7
X-Powered-By: PHP/5.4.7
Content-Length: 223879
Connection: keep-alive

Pour ce que ça vaut, j'ai essayé cela sur un standard (http) et sécurisé (https) de connexion, ni de différences: le contenu se charge correctement dans le navigateur, mais n'est pas traité par les Progrès de l'API.


Par Adam de la suggestion, j'ai essayé de commutation du côté serveur pour l'encodage gzip, sans succès ou de changement. Voici les en-têtes de réponse:

HTTP/1.1 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Content-Encoding: gzip
Content-Type: application/json
Date: Mon, 04 Mar 2013 22:33:19 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
P3P: CP="CAO PSA OUR"
Pragma: no-cache
Server: Apache/2.2.8 (Unix) mod_ssl/2.2.8 OpenSSL/0.9.8g PHP/5.4.7
X-Powered-By: PHP/5.4.7
Content-Length: 28250
Connection: keep-alive

Je le répète: le contenu est téléchargé et décodé correctement, c'est juste les progrès de l'API que je vais avoir des ennuis avec.


Par Bertrand demande, voici la demande:

$.ajax({
    url: '<url snipped>',
    data: {},
    success: onDone,
    dataType: 'json',
    cache: true,
    progress: onProgress || function(){}
});

Et voici l' onProgress gestionnaire d'événement que j'utilise (il n'est pas trop fou):

function(jqXHR, evt)
{
    // yes, I know this generates Infinity sometimes
    var pct = 100 * evt.position / evt.total;

    // just a method that updates some styles and javascript
    updateProgress(pct);
});

9voto

Jimmy Sawczuk Points 8900

Je n'étais pas en mesure de résoudre le problème de l'utilisation de onProgress sur le contenu compressé lui-même, mais je suis venu avec cette semi-simple solution de contournement. En un mot: envoyer un HEAD demande au serveur en même temps qu'un GET demande, et de rendre la barre de progression une fois il y a suffisamment d'informations pour le faire.


function loader(onDone, onProgress, url, data)
{
    // onDone = event handler to run on successful download
    // onProgress = event handler to run during a download
    // url = url to load
    // data = extra parameters to be sent with the AJAX request
    var content_length = null;

    self.meta_xhr = $.ajax({
        url: url,
        data: data,
        dataType: 'json',
        type: 'HEAD',
        success: function(data, status, jqXHR)
        {
            content_length = jqXHR.getResponseHeader("X-Content-Length");
        }
    });

    self.xhr = $.ajax({
        url: url,
        data: data,
        success: onDone,
        dataType: 'json',
        progress: function(jqXHR, evt)
        {
            var pct = 0;
            if (evt.lengthComputable)
            {
                pct = 100 * evt.position / evt.total;
            }
            else if (self.content_length != null)
            {
                pct = 100 * evt.position / self.content_length;
            }

            onProgress(pct);
        }
    });
}

Et puis de l'utiliser:

loader(function(response)
{
    console.log("Content loaded! do stuff now.");
},
function(pct)
{
    console.log("The content is " + pct + "% loaded.");
},
'<url here>', {});

Sur le côté serveur, définissez l' X-Content-Length - tête à la fois l' GET et de la HEAD des demandes (qui devraient représenter le décompressé le contenu de la longueur), et d'annuler l'envoi du contenu sur l' HEAD de la demande.

En PHP, le réglage de l'en-tête ressemble à:

header("X-Content-Length: ".strlen($payload));

Et puis annuler l'envoi du contenu, si c'est un HEAD demande:

if ($_SERVER['REQUEST_METHOD'] == "HEAD")
{
    exit;
}

Voici à quoi il ressemble en action:

screenshot

La raison pour laquelle le HEAD prend tellement de temps dans la capture d'écran ci-dessous est parce que le serveur a encore à analyser le fichier pour savoir combien de temps il est, mais c'est quelque chose que je peux améliorer, et c'est certainement une amélioration à partir de là où il était.

4voto

Ivan Castellanos Points 3714

Ne restez pas coincé juste parce qu'il n'y a pas une solution native; un hack d'une ligne peut résoudre votre problème sans toucher à la configuration d'Apache (qui dans certains hébergements sont interdites ou très limitée):

PHP à la rescousse:

var size = <?php echo filesize('file.json') ?>;

Que c'est, vous le savez probablement déjà, le reste, mais seulement comme une référence ici, c'est:

<script>
var progressBar = document.getElementById("p"),
    client = new XMLHttpRequest(),
    size = <?php echo filesize('file.json') ?>;

progressBar.max = size;

client.open("GET", "file.json")

function loadHandler () {
  var loaded = client.responseText.length;
  progressBar.value = loaded;
}

client.onprogress = loadHandler;

client.onloadend = function(pe) {
  loadHandler();
  console.log("Success, loaded: " + client.responseText.length + " of " + size)
}
client.send()
</script>

Live exemple:

Un autre de SORTE que l'utilisateur pense que je suis couché sur la validité de cette solution si elle est ici en direct: http://nyudvik.com/zip/, il est gzip-ed et le fichier réel poids de 8 MO



Liens connexes:

2voto

Adam Points 2285

Essayez de changer votre serveur d'encodage de gzip.

Votre en-tête de demande montre trois codages (gzip,deflate,sdch), de sorte que le serveur peut choisir l'un de ces trois. Par l'en-tête de réponse, nous pouvons voir que votre serveur est le choix de répondre à dégonfler.

Gzip est un format d'encodage qui comprend un dégonfler la charge utile en plus d'autres en-têtes et pied de page (qui comprend l'original non la longueur) et un autre algorithme de somme de contrôle:

Gzip sur Wikipedia

Dégonfler a certaines questions. En raison de l'héritage des questions traitant de la mauvaise algorithmes de décodage, de l'implémentation du client de dégonfler avez à courir à travers idiot vérifie juste de comprendre de mise en œuvre qui ils traitent, et malheureusement, ils sont souvent encore se tromper:

Pourquoi utiliser dégonfler au lieu de gzip pour les fichiers texte servi par Apache?

Dans le cas de votre question, le navigateur probablement voit un dégonfler fichier en viennent et juste jette ses bras et dit: "Quand je ne sais même pas exactement comment je vais finir par décodage de cette chose, comment pouvez-vous attendre de moi à vous soucier de la préparation, de l'homme?"

Si vous changez la configuration de votre serveur, donc la réponse est au format gzip (c'est à dire, gzip montre jusqu'à ce que le codage de contenu), j'ai l'espoir de votre script fonctionne comme vous l'auriez espéré ou attendu qu'il le ferait.

0voto

David Mulder Points 6381

La seule solution je pense est de compresser manuellement les données (plutôt que de laisser le serveur et le navigateur), qui vous permet d'utiliser la normale de la barre de progression et devrait vous donner des gains considérables sur la version non compressée. Par exemple, si le système n'est nécessaire pour travailler dans de dernière génération de navigateurs web vous pouvez, par exemple, zip sur le côté serveur (quelque soit la langue que vous utilisez, je suis sûr qu'il y est un zip de la fonction ou de la bibliothèque) et sur le côté client, vous pouvez utiliser zip.js. Si plus de la prise en charge du navigateur est nécessaire, vous pouvez vérifier ce AFIN de répondre à un certain nombre de compression et de décompression de fonctions (juste à choisir celui qui est pris en charge dans le côté serveur de la langue que vous utilisez). Dans l'ensemble, cela devrait être assez simple à mettre en œuvre, bien qu'il effectuera pour le pire (mais encore bien sans doute) que natif de compression/décompression. (Btw, après le donner un peu plus de réflexion, il pourrait, en théorie, faire encore mieux que la version native dans le cas où vous choisissez un algorithme de compression qui correspond au type de données que vous utilisez, et les données sont suffisamment gros)

Une autre option serait d'utiliser un websocket et charger les données dans les pièces où vous parse/poignée de chaque partie dans le même temps, il est chargé (vous n'avez pas besoin de les websockets pour cela, mais de faire 10 des requêtes http à tour de rôle peut être tout à fait une corvée). Si c'est possible dépend du scénario, mais pour moi, ça sonne comme des données de rapport est le genre de données qui peuvent être chargés dans les parties et n'est pas nécessaire d'être le premier entièrement téléchargé.

-2voto

Bertrand Points 340

Je ne pas bien comprendre le problème, il ne devrait pas arriver depuis la décompression doit fait par le navigateur.

Vous pouvez essayer de s'éloigner de jQuery ou hack jQuery parce que le $.ajax ne semble pas bien fonctionner avec des données binaires:

Ref: http://blog.vjeux.com/2011/javascript/jquery-binary-ajax.html

Vous pouvez essayer de faire votre propre mise en œuvre de la requête ajax Voir: https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest#Handling_binary_data

Vous pourriez essayer de décompresser le json le contenu en javascript (voir ressources dans les commentaires).

* Mise à JOUR 2 *

l' $.ajax fonction ne prend pas en charge les progrès de gestionnaire d'événement ou il ne fait pas partie de l'jQuery documentation (voir commentaire ci-dessous).

ici est un moyen d'obtenir ce gestionnaire de travail, mais je n'ai jamais essayé moi-même: http://www.dave-bond.com/blog/2010/01/JQuery-ajax-progress-HMTL5/

* Mise à JOUR 3 *

La solution d'utiliser tierce tierce partie de la bibliothèque de prolonger (?) jQuery ajax fonctionnalité, donc, ma suggestion ne s'appliquent pas

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