tomcat - запустить поток в сервлете и отправить ответ

Как я могу запустить поток, который будет работать в фоновом режиме, но ответ будет немедленно отправлен пользователю?

Я пытаюсь загрузить файл с устройства Android. Хотя процесс загрузки работает нормально, я хотел бы сообщить пользователю, что процесс завершен, как только файл был загружен с его конца.

Я проверил, что вызов ServletFileUpload.parseRequest() занимает много времени.

        ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory);

        List items = uploadHandler.parseRequest(request);

Итак, в основном я ищу способ, с помощью которого я могу сказать пользователю: «Эй, ты сделал свою часть работы», теперь очередь сервлета выполнять всю обработку.

но я не могу, потому что блокируется parseRequest.

Я ошибаюсь в том, что он блокируется, потому что именно тогда он фактически отправляется устройством?

Загрузка на Android:

        while (bytesRead > 0)
        {
            task.myPublishProgress(totalBytesRead, fileSize);

            dos.write(buffer, 0, bufferSize);

            bytesAvailable = fileInputStream.available();

            bufferSize = Math.min(bytesAvailable, maxBufferSize);

            bytesRead = fileInputStream.read(buffer, 0, bufferSize);

            totalBytesRead+= bytesRead;

        }

        // send multipart form data necesssary after file data...
        dos.writeBytes(lineEnd);

        dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        // close streams

        fileInputStream.close();

        dos.flush();

        Log.i(TAG ,"Database file with name " + this.fileName + " has been successfully written");

        InputStream is = conn.getInputStream();

        // retrieve the response from server

        int ch;

        StringBuilder b = new StringBuilder();

        while( ( ch = is.read() ) != -1 ){
            b.append( (char)ch );
        }

        dos.close();

обработка на сервере:

                DiskFileItemFactory  fileItemFactory = new DiskFileItemFactory ();

            ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory);
            /*
             * Parse the request
             */
            System.out.println("parsing the request....");
            **List items = uploadHandler.parseRequest(request);**
            FileItem item = (FileItem) items.get(0);
            fileName = item.getName();

            File file = new File(destinationDir, fileName);
            item.write(file);

Я бы хотел, чтобы сервлет не слишком блокировал parseRequest, а, возможно, обрабатывал это в потоке и отправлял ответ клиенту.

Благодарность


person kkudi    schedule 21.03.2011    source источник
comment
Не могли бы вы дать более подробную информацию о том, что вы пытаетесь сделать, и где вы поражены?   -  person anubhava    schedule 21.03.2011
comment
@anubhava см. отредактированный вопрос. дайте мне знать, если вы хотите получить более подробную информацию.   -  person kkudi    schedule 21.03.2011
comment
@kkudi: Спасибо, я мало что знаю о кодировании Android, но есть ли вариант, когда устройство отправляет запрос AJAX на ваш сервер? Таким образом, вам не придется заниматься созданием этого решения на стороне сервера.   -  person anubhava    schedule 21.03.2011
comment
Устройство отправляет HttpURLConnection, записывает данные на сервер и ожидает ответа.   -  person kkudi    schedule 21.03.2011
comment
Хорошо, в таком случае вы можете использовать асинхронный http-клиент? Пожалуйста, проверьте https://github.com/AsyncHttpClient/async-http-client.   -  person anubhava    schedule 21.03.2011
comment
Я думаю, мне нужен асинхронный сервер, а не клиент или способ, которым вызов метода parseRequest обрабатывается асинхронно. Возможно, в другой ветке (поэтому и задаю свой вопрос в первую очередь). Ну, я мог просто вообще не получить ответа от клиента, и это каким-то образом решило бы мою проблему.   -  person kkudi    schedule 21.03.2011
comment
Проблема с выполнением этого на стороне сервера заключается в том, что вы не хотите зависать на запросе, но все же хотите вернуть ответ (всякий раз, когда он доступен). Если запрос был выполнен, вы не сможете ответить клиенту, используя тот же запрос.   -  person anubhava    schedule 21.03.2011


Ответы (1)


Это невозможно в стандартном HTTP. Ответ может быть отправлен только тогда, когда тело запроса полностью использовано. Пока вы не позвоните uploadHandler.parseRequest(request);, файлы не будут загружены. Вы бы солгали, если бы сказали: «Все готово!» до этого момента.

Только после вызова uploadHandler.parseRequest(request) и получения элементов вы можете честно сказать пользователю, что он сделал. Однако остаток может быть выполнен в отдельном потоке. Я бы предложил создать фиксированный пул потоков для того, которым управляет ServletContextListener. Однако я думаю, что это немного преувеличено, если вы не выполняете никаких дорогостоящих задач с загруженными файлами.

Что касается конкретной проблемы (что приложение, кажется, ничего не делает, пока оно занято загрузкой файлов), я бы предпочел решить проблему на стороне клиента. Получите размер файлов, получите количество байтов, отправленных в выходной поток, а затем покажите индикатор выполнения на стороне клиента, чтобы клиент был проинформирован о ходе выполнения.

person BalusC    schedule 21.03.2011
comment
это именно то, что я делаю. Я показываю диалоговое окно «Ход выполнения», используя байты, отправленные в выходной поток, но это все. Вы сами сказали, что я буду лгать, что файлы были загружены, если я просто скажу пользователю, что процесс загрузки завершен только путем обновления диалоговое окно прогресса, использующее количество байтов, записанных в выходной поток. Это не означает, что процесс загрузки прошел успешно до тех пор, пока запрос не будет обработан и ответ не будет отправлен клиенту. - person kkudi; 21.03.2011
comment
Это неправда. Кажется, вы думаете, что все файлы уже были отправлены на сервер в момент вызова doPost() сервлета. Это неправда. Прямо перед вызовом parseRequest() файлы все еще находятся на клиенте. Как только вы вызовете parseRequest(), сервер начнет читать тело запроса с загруженными файлами, и, таким образом, начнется настоящая потоковая передача байтов. Как только метод parseRequest() завершится и код продолжит работу, все файлы будут прочитаны. - person BalusC; 21.03.2011
comment
Ах я вижу! Итак, как мне получить количество байтов, фактически обработанных parseRequest? Единственная информация, которую я использую для диалога хода выполнения, — это количество байтов, прочитанных в DataOutputStream, как показано в коде клиента. - person kkudi; 21.03.2011
comment
Создайте HttpServletRequestWrapper, который оборачивает HttpServletRequest#getInputStream() в CountingInputStream (часть commons-io, которая у вас уже должна быть). Сохраните ссылку на него в сеансе перед синтаксическим анализом и удалите его после синтаксического анализа. Затем запускайте асинхронные/ajax-запросы к некоторому сервлету, который проверяет его в сеансе и возвращает статус. - person BalusC; 21.03.2011