GZIPOutputStream неправильно сжимает строку для ответа HTTP - PullRequest
2 голосов
/ 29 февраля 2012

Я пишу простой http-сервер Java, который отвечает данными JSON. Я пытаюсь сжать данные перед отправкой, но обычно они отправляют обратно сжатые данные, которые выдают ошибку в браузере. Например, в Firefox написано:

Ошибка кодирования содержимого Невозможно отобразить страницу, которую вы пытаетесь просмотреть, поскольку она использует недопустимую или неподдерживаемую форму сжатия.

Иногда это работает, если строка, которую я сжимаю, небольшая без определенных символов, но кажется, что она запуталась, когда есть квадратные скобки и т. Д. В частности, приведенный ниже пример текста не работает.

Это какая-то проблема кодировки символов? Я пробовал все что угодно, но просто не хочет работать легко.

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();

Ответы [ 2 ]

2 голосов
/ 29 февраля 2012

ОБНОВЛЕНИЕ: Это больше не правильный ответ, см. Ответ @amichair выше.

Против интуитивно, я не думаю, что GZIPOutputStream подходит для потоковой передачи. Попробуйте это:

...
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
1 голос
/ 07 июня 2018

Принят неправильный ответ.

GZIPOutputStream действительно может использоваться для реализации gzip кодирования содержимого в HTTP. Фактически, именно так я и реализовал это на облегченном HTTP-сервере JLHTTP . Поддержка кодировки контента deflate идентична, вместо нее используется DeflaterOutputStream. Проблема с приведенным выше кодом заключается просто в том, что он глючит: -)

  • Все операторы println (включая оператор внизу) следует заменить на print и явный \r\n в конце строки. Это связано с тем, что символы новой строки, напечатанные println, зависят от платформы, например, в Linux он будет печатать только \n, тогда как для HTTP требуется полный CRLF (\r\n).

  • out.print(outZip) в основном вызывает outZip.toString() и выводит это в поток. Тем не менее, outZip содержит сжатые двоичные данные, поэтому преобразование их в строку (с использованием произвольной кодировки по умолчанию платформы, не менее) очень вероятно повредит данные.

  • Код берет строку, преобразует ее в байты, сжимает их, преобразует их обратно в строку, преобразует их обратно в байты и записывает их. Вместо этого нужно только преобразовать строку в байты, сжать их и записать. Для этого вам также не нужен ByteArrayOutputStream, GZIPOutputStream может напрямую обернуть основной выходной поток. Только не забудьте очистить поток печати после заголовков (и завершающий CRLF), и только потом начинать со сжатого потока для тела.

  • Закрытие ресурсов должно выполняться в блоках finally или try-with-resources и с правильным порядком и временем.

  • В этом примере соединение закрывается в конце потока, что нормально. Но в целом, если вы хотите сохранить соединение и потоковую передачу потенциально больших данных с неизвестной длиной (заранее не знаете сжатый размер), вам также необходимо реализовать кодировку передачи chunked (это довольно просто) .

С исправленным кодом GZIPOutputStream работает как брелок.

Однако, хотя он отлично подходит для образовательных целей, обратите внимание, что это , а не HTTP-сервер, даже если он исправлен. Далее вы можете прочитать RFC 2616 или 7230, чтобы узнать, что еще требуется для HTTP ... но зачем изобретать weel? Существует множество облегченных встраиваемых HTTP-серверов, которые можно использовать для правильного выполнения работы без особых усилий, в том числе JLHTTP .

...