Предотвратите тайм-аут клиента, когда сервлет генерирует большую загрузку

#java #http #servlets

Вопрос:

У меня есть сервлет Java, который генерирует некоторый произвольный файл отчета и возвращает его в качестве загрузки в браузер пользователя. Файл записывается непосредственно в выходной поток сервлета, поэтому, если он очень большой, он может успешно загружаться по частям. Однако иногда результирующие данные недостаточно велики, чтобы их можно было разделить на куски, и либо время ожидания клиентского подключения истекает, либо оно выполняется успешно, но загрузка не отображается в пользовательском интерфейсе браузера, пока она не будет выполнена на 100%.

Вот как выглядит мой код сервлета:

 @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("application/pdf");
    response.setHeader("Content-Disposition", "attachment; filename=""   report.getFileName(params)   """);

    try(OutputStream outputStream = response.getOutputStream()) {
        // Send the first response ASAP to suppress client timeouts
        response.flushBuffer(); // This doesn't seem to change anything??

        // This calls some arbitrary function that writes data directly into the given stream
        generateFile(outputStream);
    }
}
 

Я провел особенно плохой тест, в котором генерация файлов заняла 110 826 мс. Как только он подошел к концу, клиент загрузил файл размером 0 байт — я предполагаю, что это результат тайм-аута. Я ожидаю, что этот конкретный результат будет где — то между 10 и 30 КБ-меньше, чем буфер сервлета. Когда я запустил другой тест, он быстро сгенерировал много данных (всего до 80 МБ), поэтому загрузка появилась в моем браузере после заполнения первого фрагмента.

Есть ли способ заставить загруженный файл появиться в браузере (и предотвратить тайм-аут) до того, как будут сгенерированы какие-либо фактические данные? На правильном ли я пути с этим flushBuffer() звонком?

Комментарии:

1. Является ли Клиент браузером? Тогда у вас есть очень хорошие инструменты отладки, доступные вам в клиенте.

2. Когда я пытаюсь загрузить URL-адрес сервлета (в качестве запроса GET) на новой вкладке с открытой сетевой панелью отладчика, я вижу, что запрос остается в состоянии ожидания до тех пор, пока первый фрагмент (большого файла) или весь файл (для небольших заданий) не будет завершен и загружен. Я не уверен, что еще мне следует искать со стороны клиента. Совершенно ясно, что сервер ничего не возвращает в тот момент, когда я звоню flushBuffer() .

3. Вам необходимо периодически очищать буфер; например, если вы генерируете данные из запроса БД, извлеките страницу, запишите ее в выходной поток, а затем очистите ее. Если вы рассчитываете generateFile занять некоторое время, остерегайтесь тайм — аутов транзакций БД-вам придется разделить свою работу на отдельные транзакции.

4. Хорошо. Я попробовал тестовый случай, когда я писал CSV-файл, и сбрасывал поток вывода после каждой строки (63 строки, всего 24 КБ). Это может помочь, хотя меня беспокоит нагрузка на сервер, связанная с отправкой ответа в таком количестве фрагментов. Можно ли заставить браузер запустить процесс загрузки только с заголовками и 0 байтами содержимого?

Ответ №1:

Ну, похоже, что уменьшение размера моего выходного буфера response.setBufferSize(1000); позволило успешно загрузить файл стресс-теста. Я до сих пор не знаю, почему response.flushBuffer() , похоже, ничего не сделал, но, по крайней мере, пока я генерирую данные достаточно быстро, чтобы заполнить этот размер буфера до истечения времени ожидания, загрузка будет завершена.