Question

J'écris un simple serveur http Java qui répond avec des données JSON.J'essaie de GZip les données avant de les envoyer, mais cela renvoie généralement des données gzippées qui produisent une erreur dans le navigateur.Par exemple, dans Firefox, il est écrit :

Erreur de codage de contenu La page que vous essayez de visualiser ne peut pas être affichée car elle utilise une forme de compression non valide ou non prise en charge.

Parfois, cela fonctionne si la chaîne que je compresse est petite sans certains caractères, mais cela semble gâcher lorsqu'il y a des crochets, etc.En particulier, l'exemple de texte que j'ai ci-dessous échoue.

Est-ce une sorte de problème d'encodage des caractères ?J'ai essayé toutes sortes de choses, mais ça ne veut tout simplement pas fonctionner facilement.

String text;            
private Socket server;
DataInputStream in = new DataInputStream(server.getInputStream());
PrintStream out = new PrintStream(server.getOutputStream());

while ((text = in.readLine()) != null) {
    // ... process header info
    if (text.length() == 0) break;
}

out.println("HTTP/1.1 200 OK");
out.println("Content-Encoding: gzip");
out.println("Content-Type: text/html");
out.println("Connection: close");


// x is the text to compress
String x = "jsonp1330xxxxx462022184([[";
ByteArrayOutputStream outZip = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outZip);

byte[] b = x.getBytes(); // Changing character encodings here makes no difference

gzip.write(b);
gzip.finish();
gzip.close();
outZip.close();
out.println();
out.print(outZip);
server.close();
Était-ce utile?

La solution

La réponse acceptée est incorrecte.

GZIPOutputStream peut en effet être utilisé pour mettre en œuvre gzip encodage du contenu en HTTP.En fait, c'est exactement comme ça que je l'ai implémenté dans le JLHTTP serveur HTTP léger.Soutien deflate l'encodage du contenu est identique, juste avec DeflaterOutputStream utilisé à la place.Le problème avec le code ci-dessus est simplement qu'il est buggé :-)

  • Tous println les déclarations (y compris celle du bas) doivent être remplacées par print et un explicite \r\n à la fin de la chaîne.C'est parce que les caractères de nouvelle ligne imprimés par println dépendent de la plate-forme, donc par ex.sous Linux, il n'imprimera qu'un \n, alors que HTTP nécessite un CRLF complet (\r\n).

  • out.print(outZip) appelle essentiellement outZip.toString() et l'imprime sur le flux.Cependant, outZip contient des données binaires compressées, donc leur conversion en chaîne (en utilisant le codage arbitraire par défaut de la plate-forme, rien de moins) est très susceptible de corrompre les données.

  • Le code prend la chaîne, la convertit en octets, la compresse, la reconvertit en chaîne, la reconvertit en octets et l'écrit.Au lieu de cela, il lui suffit de convertir la chaîne en octets, de les compresser et de les écrire.Vous n'avez pas besoin du ByteArrayOutputStream pour ça non plus, le GZIPOutputStream peut envelopper directement le flux de sortie sous-jacent.N'oubliez pas de vider le flux d'impression après les en-têtes (et le CRLF final), puis de commencer ensuite par le flux compressé pour le corps.

  • La fermeture des ressources doit être effectuée dans des blocs final ou try-with-resources, et dans le bon ordre et dans le bon timing.

  • Dans cet exemple, la connexion est fermée à la fin du flux, ce qui est très bien.Mais en général, si vous souhaitez maintenir la connexion active et diffuser des données potentiellement volumineuses de longueur inconnue (vous ne connaissez pas à l'avance la taille compressée), vous devez implémenter le chunked transférer également l'encodage (c'est assez simple).

Avec le code corrigé, GZIPOutputStream fonctionne à merveille.

Cependant, bien qu'il soit idéal à des fins éducatives, veuillez noter qu'il s'agit d'un pas un serveur HTTP, même s'il est corrigé.Vous pouvez lire davantage la RFC 2616 ou 7230 pour savoir ce que HTTP doit faire d'autre...mais pourquoi réinventer la roue ?Il existe de nombreux serveurs HTTP intégrables légers que vous pouvez utiliser pour effectuer le travail correctement avec peu d'effort, JLHTTP parmi eux.

Autres conseils

mise à jour: ce n'est plus la bonne réponse, voir la réponse par @Amichair ci-dessus.

Contre-intuitivement, je ne pense pas que GzipOutputStream convient au streaming.Essayez ceci:

...
out.println("Content-Encoding: deflate");  // NOTICE deflate encoding
out.println("Content-Type: text/html");
out.println("Connection: close");
out.println();
String x = "jsonp1330xxxxx462022184([[";
DeflaterInputStream dis = new DeflaterInputStream(out);
dis.write(x.getBytes("utf-8"));   // JSON is UTF-8
dis.close();
server.close(); //  this a bad idea, the client may not have read the data yet

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top