Nous avons un système dans lequel un client effectue une demande HTTP GET, le système effectue un certain traitement sur le backend, vous transfère les résultats et l'envoie au client. Étant donné que le traitement peut prendre un certain temps, nous envoyons ceci sous forme d'un Cependant, lorsque nous avons une quantité exceptionnellement petite de données dans le premier J'ai essayé de décaper le code sur les os nures pour donner un exemple: p> i Définir mon temps de navigateur doit être d'environ 20 secondes pour une reproduction facile . En dépit de la rédaction des 100 octets à quelques reprises, rien n'est envoyé au navigateur et à l'époque du navigateur. Si j'élans le délai d'attente du navigateur, rien n'est envoyé jusqu'à ce que 1 000 octets aient été écrits, puis le navigateur apparaît sur la boîte de dialogue "Voulez-vous enregistrer ...". Encore une fois, après les 1000 octets initiaux, chaque addition 100 octets envoie fin, plutôt que de tamponner jusqu'à 1000 morceaux d'octets. P> Si je règle le nombre d'octets max dans la condition de la pièce à 200 ou plus, cela fonctionne bien, Envoi de seulement 200 octets. P> Que puis-je faire pour forcer le servlet à renvoyer de très petites quantités initiales de données? p> p> zipOutputStream code> d'emballage de la réponse
.getoutoutToutPutStream () code>.
zipenterry code>, et la deuxième entrée prend beaucoup de temps, le navigateur utilise le client des temps. Nous avons essayé de rincer le tampon de flux, mais aucune réponse ne semble être envoyée au navigateur jusqu'à ce que au moins 1 000 octets aient été écrits dans le flux. Curieusement, une fois que les 1000 premiers octets ont été envoyés, des flushes ultérieures semblent fonctionner correctement. p>
6 Réponses :
Je suppose que le flux de sortie ZIP n'écrit pas réellement quoi que ce soit avant d'être capable de compresser des choses. HUFFMANN ALGORITHM utilisé pour la zippation nécessite que toutes les données soient connues avant d'être capables de compresser quoi que ce soit. Il ne peut pas commencer avant que tout est connu essentiellement. p>
La zipping peut être une victoire si la quantité de données est grande, mais je ne pense pas que vous puissiez obtenir une réponse asynchrone lors de la zippation de données. P>
Je pensais que cela pourrait être le cas aussi, mais je peux reproduire cela avec un FilterOutPutPutStream code>. De plus, étant donné que chaque écriture est un
zipenterry code>, et ceux-ci sont codés séparément, il ne devrait pas attendre plus à zip car le flux a une entrée entière. Enfin, cela n'explique pas pourquoi il commence soudainement à envoyer 100 morceaux d'octets après les 1000 premiers octets
Parce que Huffmann Algorithm doit calculer réellement l'alphabet le code utilisé pour zip. Cela pourrait être pourquoi vous attendez le premier morceau. Après cela, cela peut encoder progressivement toutes les données.
Merci pour votre réponse, mais avez-vous manqué la partie où changer le zipOutputStream code> sur un
filtreOutPutPutStream code> a le même problème? Je peux même reproduire ce comportement avec le flux de sortie de réponse, qu'ils envoient des données binaires ou textuelles. Je ne pense pas que la question soit liée à la zippation des données, c'est-à-dire où j'ai rencontré le problème.
@Snicolas: S'il vous plaît lire ceci et envisagez de réparer toutes vos réponses précédentes.
@Ballusc, désolé je ne l'attrape pas. Signatures?
Vous pouvez être vissé par l'API Java.
regarder à travers les Javadocs de la diversité de la famille de classes de sortie ( OUTPUTSTREAM , servletOutPutStream a>, FilterOutPutStream et ZipOutPutStream ), ils Soit mentionner qu'elles comptent sur le flux sous-jacent de chasse () ou ils déclarent que Flush () ne fait rien (sortie de sortie). p>
du filtreOutPutStream Javadoc: P> La méthode de flush de filtreOutPutStream appelle la méthode de flush de son
flux de sortie sous-jacent. p>
BlockQuote> Dans le cas de ZipOutPutStream, il est enveloppé autour du flux revenu de servleResponse.getoutPutStream () qui est un servletOutPutStream. Il s'avère que le servletOutputStream ne met pas en œuvre flush () non plus, il le hérite de Provitenstream qui mentionne spécifiquement dans son Javadoc: P> peut-être ceci est un cas particulier, je n'ai pas 't sais. Je sais que Flush () a longtemps été longtemps et il est peu probable que personne n'a remarqué un trou dans la fonctionnalité là-bas. P> Cela me fait me demander s'il y a un composant système d'exploitation au flux mise en mémoire tampon pouvant être configurée pour éliminer l'effet tampon 1k. p> a Question associée A> a un problème similaire mais travaillait directement avec un fichier au lieu d'une abstraction de flux de Java et Cette réponse pointe vers Les articles MSDN impliqués concernant tampon de fichier et Caching de fichier . P> < p> A Scénario similaire a été répertorié dans la banque de bogues ASE. P> La bibliothèque Java IO s'appuie sur la mise en œuvre du système d'exploitation pour les flux. Si la mise en cache du système d'exploitation est allumée, le code Java peut ne pas être en mesure de forcer un comportement différent. Dans le cas de Windows, vous devez ouvrir le fichier et envoyer des paramètres non standard pour permettre une fonctionnalité de cache-écriture ou de cachette sans buffère. Je doute que le SDK Java fournit de telles options spécifiques au système d'exploitation car ils essaient de créer des API génériques de plate-forme. P> P>
Résumé H2>
Non, comme les snicolas répondit, la zippation d'un flux nécessite le flux entier B> étant complètement en mémoire. Ce n'est pas possible / conseillé de rincer les deux.
@Ballusc - Je voulais dire pour le scénario lorsqu'il utilisait FilterOutPutStream. Il doit faire la petite page de jeu de page C'est son propre étui de test afin qu'il puisse isoler les différentes couches (navigateur / serveur).
Oh, je vois, vous commenciez essentiellement sur un commentaire au lieu de répondre à la question.
J'ai le même problème dans IE9, FF5 et Chrome; Il semble être un navigateur agnostique. J'ai récemment essayé Firebug et que la question semblait être comme je l'ai décrite, mais j'essaierai Fiddler aussi et ferai rapport.
Vient de vérifier avec violon. Je reçois une seule réponse du serveur, mais ce ne sont que des données d'en-tête - Aucune des données réelles ne reviennent, ce qui entraîne le guichet unique. Si je prolonge le délai d'attente, je reçois le comportement que j'ai vu auparavant, où aucune donnée ne reviendra du serveur avant d'atteindre un montant arbitraire qu'il a atteint et qu'il glisse en réalité. Essayé avec un FilterOutPutTream code> pour éviter toute préoccupation avec la zippation étant l'erreur (bien que je doute que c'était l'erreur de commencer).
i entièrement, je ne peux pas reproduire votre problème. Vous trouverez ci-dessous votre code, légèrement modifié, en cours d'exécution dans un serveur de jeton incorporé. Je l'ai couru à Intellij et demanda http: // localhost: 8080 de Firefox. Comme prévu, la boîte de dialogue "Enregistrer ou ouvrir" est apparu après 1 seconde. Sélection de "Enregistrer" et en attente de 20 secondes dans un fichier ZIP pouvant être ouvert et contient 20 entrées distinctes, nommée FOO import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZippingAndStreamingServlet {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
context.addServlet(new ServletHolder(new BufferingServlet()), "/*");
server.start();
System.out.println("Listening on 8080");
server.join();
}
static class BufferingServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
ZipOutputStream _zos = new ZipOutputStream(response.getOutputStream());
ZipEntry _ze;
long startTime = System.currentTimeMillis();
long _lByteCount = 0;
int count = 1;
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=my.zip");
while (_lByteCount < 2000) {
_ze = new ZipEntry("foo" + count);
_zos.putNextEntry(_ze);
byte[] bytes = String.format("%100d", count++).getBytes();
System.out.println("Sending " + bytes.length + " bytes");
_zos.write(bytes);
_lByteCount += bytes.length;
sleep(1000);
System.out.println("Zip: " + _lByteCount + " Time: " + ((System.currentTimeMillis() - startTime) / 1000));
_zos.flush();
}
_zos.close();
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new IllegalStateException("Unexpected interrupt!", e);
}
}
}
}
Il s'avère qu'il existe une limite sur la pile Apache / Windows IP sous-jacente qui tamponne les données d'un flux dans une tentative d'efficacité. Étant donné que la plupart des gens ont le problème de trop de données em>, pas le problème des données trop peu em>, c'est juste la plupart du temps. Ce que nous avons fini par faire était de demander à l'utilisateur de demander suffisamment de données que nous appuions la limite de 1000 octets avant de terminer. Désolé de prendre si longtemps pour répondre à la question. P>
Si vous avez des références pour cela, je voudrais les voir. Je suis confronté à un problème similaire, il serait donc bon d'obtenir d'autres informations sur la façon dont il est un problème.
Je sais que c'est une question vraiment très ancienne, mais pour le compte rendu, je voulais poster une réponse qui devrait être une solution pour la question que vous rencontrez.
La clé est que vous voulez affleurer Le flux de réponses, pas le flux zip. Parce que le flux zip ne peut pas affleurer ce qui n'est pas encore prêt à écrire. Votre client, comme vous l'avez mentionné, est chronométré car il ne reçoit pas de réponse dans une durée prédéterminée, mais une fois qu'il reçoit des données, il est patient et attendra très longtemps pour télécharger le fichier, donc le correctif est facile. , à condition que vous rincez le courant correct. Je recommande ce qui suit: p> Maintenant, ce qui devrait arriver ici, est l'en-tête et les codes de réponse seront commis avec quelque chose dans la sortie de la mémoire tampon de réponse. Cela ne ferme pas le flux, de sorte que tout écrit supplémentaire au flux est annexé. L'inconvénient de le faire de cette façon, c'est que vous ne pouvez pas connaître la longueur du contenu à attribuer à l'en-tête. Le positif est que vous commencez immédiatement au téléchargement et que vous n'autorisez pas le navigateur de temps d'attente. P> p>
Le problème est que, par défaut, chaque implémentation de servlet soumise aux données, tandis que SSE et autres exigences personnalisées peuvent / nécessiteront des données immédiatement.
La solution consiste à effectuer les éléments suivants: P>
response.setBufferSize(1) // or some similar small number for such servlets.
Dans quel système d'exploitation fonctionnez-vous? J'ai vu un problème similaire où un servlet pour un rapport ne retournerait rien à moins que la taille de la sortie était> 1024 octets. En regardant les Javadocs, cela pourrait être lié à la manière dont le système d'exploitation gère ses tampons.
Win7. Nous avons finalement décidé que ce n'était pas du côté de l'application et a forcé la demande à accepter uniquement les demandes avec une certaine quantité de données.
Je crois que cela pourrait être un problème de serveur d'applications. S'il vous plaît, voir Mon message .