Как получить сжатые данные JSON с помощью XMLHttpRequest с java-сервера

Я запрашиваю и анализирую данные json, используя XMLHttpRequest. Сервер Java 8, использующий встроенный причал 9, получает запрос и возвращает данные. Код показан ниже с javascript на стороне клиента в первом блоке и кодом java на стороне сервера во втором.

var http = new XMLHttpRequest();
http.onreadystatechange=function() {
  if (this.readyState == 4) {
    if (this.status == 200) {
      data = JSON.parse(this.responseText);
    }
  }
};
http.open("GET", "/mydata", true);
http.send();

 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

  response.setContentType("application/json");
  response.setStatus(Response.SC_OK);
  PrintWriter pw = response.getWriter();
  pw.write(myDataInJsonStringFormat);
  pw.close();
  baseRequest.setHandled(true);
}

Вопрос в том, как я могу изменить приведенный выше код для отправки сжатых данных json из java, а затем распаковать данные с помощью XMLHttpRequest?

Я бы предпочел не использовать какие-либо дополнительные клиентские или серверные библиотеки вместо того, что я уже использую.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
735
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Добавьте Eclipse Jetty GzipHandler в дерево обработчиков сервера в место перед кодом пользовательского обработчика.

Это будет выполнять 2 задачи:

  • Он распаковывает входящее сжатое содержимое тела запроса.
  • Он будет сжимать содержимое тела исходящего ответа

Просто убедитесь, что вы настроили список включенных mime-типов в GzipHandler, чтобы разрешить обработку application/json содержимого тела ответа.

Если я смогу настроить это, что мне делать со сжатыми данными в ответе XMLHttpRequest?

paulh 24.04.2019 12:48

В данных XMLHttpResponse ничего делать не нужно, см. ниже. Этот ответ был бы подходом на стороне сервера, если бы возвращаемые данные json динамически менялись, а не были более статичными по своей природе.

paulh 29.04.2019 13:41

Вы можете использовать base64 для передачи данных. В нем есть две функции соответственно для декодирования и кодирования строк base64: atob() и btoa(). Найдите, как вы можете сделать это с помощью java.

Хотя это можно сделать как на стороне java, так и на стороне javascript, не увеличит ли это размер данных json, как это предлагается в примере на w3schools.com/jsref/met_win_atob.asp.

paulh 24.04.2019 12:28

Данные, которые я отправляю с Java-сервера клиенту, будут иметь размер до 17 МБ, и это часто происходит из-за плохого мобильного соединения.

paulh 24.04.2019 12:30

В этом случае вы можете использовать JSONC для сжатия строки JSON: JSONC.compress(json); JSONC.decompress(сжатыйJSON);

Mychell Teixeira 24.04.2019 16:14
Ответ принят как подходящий

В этом случае одни и те же наборы данных повторно передаются клиентам. Вместо добавления обработчика причала текстовая строка json преобразуется в сжатый массив байтов:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPOutputStream;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(bos);
OutputStreamWriter osw = new OutputStreamWriter(gzip, StandardCharsets.UTF_8);
osw.write(myDataInJsonStringFormat);
osw.close();

compressedJsonData = bos.toByteArray();

Затем данные возвращаются Java-клиенту обработчиком причала:

public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

  response.setContentType("application/json");
  response.setHeader("Content-Encoding", "gzip");
  response.setContentLength(compressedJsonData .length);

  response.setStatus(Response.SC_OK);

  response.getOutputStream().write(compressedJsonData );
  response.getOutputStream().close();

  baseRequest.setHandled(true);
}

В коде JavaScript XMLHttpRequest на стороне клиента никаких изменений не требуется. Браузеры (проверено в Edge, IE11, Chrome) автоматически распаковывают json и преобразуют его обратно в текст перед передачей в это.responseText :

var http = new XMLHttpRequest();
http.onreadystatechange=function() {
  if (this.readyState == 4) {
    if (this.status == 200) {
      data = JSON.parse(this.responseText);
    }
  }
};
http.open("GET", "/mydata", true);
http.send();

Я также добавил индикатор выполнения, чтобы показать клиенту, сколько данных было загружено. Для этого длина до сжатия сохраняется и добавляется в качестве пользовательского заголовка http на стороне сервера.

response.setHeader("Uncompressed-Length", myDataInJsonStringFormat.length + "");

На клиенте этот заголовок считывается в обработчике onreadystatechange как расчетная длина, когда состояние готовности == 2 и обработчик выполнения настроены следующим образом:

    http.onprogress = function(event) {
      var percentComplete = Math.round((event.loaded/estimatedLength) * 100);
      progressMessage = Math.round(event.loaded/1000) + "KB downloaded and uncompressed, " + percentComplete + "%";
    };

Если вы используете Jetty GzipHandler, он учитывает различные заголовки кеша между браузером, любым посредником, вашим сервером Jetty и любыми сервлетами, которые устанавливают эти заголовки. Подход, который у вас есть в этом ответе, не подходит. Ваш контент будет неоднократно сжиматься (когда в этом нет необходимости) и повторно отправляться клиенту (когда в этом нет необходимости).

Joakim Erdfelt 29.04.2019 13:52

Возможно, мне следовало добавить, что я отправляю заголовки response.setHeader("Cache-Control", "public,max-age=1209600"); // 2 недели и response.setHeader("Expires", new Date(System.currentTimeMillis() + 1209600000).toString()); но я опустил это, так как вопрос был о сжатии. Контент правильно кэшируется как в сервис-воркерах, так и в кешах браузера, и это было подтверждено мониторингом журналов сервера и отсутствием новых запросов.

paulh 29.04.2019 14:18

Да, управление кешем старой школы. Имейте в виду, что GzipHandler также поддерживает запросы ETag и range (на тот случай, когда они вам в конечном итоге понадобятся).

Joakim Erdfelt 29.04.2019 15:11

Спасибо за подсказку, ETag может хорошо работать с моей конкретной реализацией и вполне может использоваться в будущем.

paulh 29.04.2019 16:02

Другие вопросы по теме