Вопрос

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

На данный момент определения методов загрузки и скачивания выглядят следующим образом (написаны с использованием Apache CXF):

boolean uploadFile(@WebParam(name = "username") String username,
    @WebParam(name = "password") String password,
    @WebParam(name = "filename") String filename,
    @WebParam(name = "fileContents") byte[] fileContents)
    throws UploadException, LoginException;

byte[] downloadFile(@WebParam(name = "username") String username,
    @WebParam(name = "password") String password,
    @WebParam(name = "filename") String filename) throws DownloadException,
    LoginException;

Таким образом, файл загружается и скачивается как массив байтов.Но если у меня есть файл какого-то дурацкого размера (например.1 ГБ), конечно, это попытается поместить всю эту информацию в память и привести к сбою моего сервиса.

Итак, мой вопрос: можно ли вместо этого вернуть какой-то поток?Я предполагаю, что это не будет сильно зависеть от ОС.Хотя я знаю теорию веб-сервисов, мне все еще нужно получить немного информации о практической стороне.

Приветствия за любой вклад, Ли

Это было полезно?

Решение

Стивен Денн имеет реализацию Metro, удовлетворяющую вашим требованиям.Мой ответ представлен ниже после краткого объяснения, почему это так.

Большинство реализаций веб-служб, созданных с использованием HTTP в качестве протокола сообщений, совместимы с REST, поскольку они допускают только простые шаблоны отправки-получения и не более того.Это значительно улучшает совместимость, поскольку все различные платформы могут понимать эту простую архитектуру (например, веб-служба Java взаимодействует с веб-службой .NET).

Если вы хотите сохранить это, вы можете обеспечить фрагментирование.

boolean uploadFile(String username, String password, String fileName, int currentChunk, int totalChunks, byte[] chunk);

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

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

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

JAX-WS RI обеспечивает поддержку отправки и получения больших вложений в потоковом режиме.

  • Используйте MTOM и DataHandler в модели программирования.
  • Приведите DataHandler к StreamingDataHandler и используйте его методы.
  • Обязательно вызовите StreamingDataHandler.close(), а также закройте поток StreamingDataHandler.readOnce().
  • Включите разбиение HTTP на стороне клиента.

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

Это логично, поскольку стандартизированные веб-сервисы полагаются на http-протокол.Тот, кто «без гражданства», скажет, что он работает как «открытое соединение…послать запрос ...получить данные...закрыть запрос».В любом случае соединение будет закрыто в конце.Поэтому что-то вроде потоковой передачи здесь не предполагается использовать.Или он находится над http (например, веб-сервисы).

Извините, но, насколько я понимаю, потоковая передача в веб-сервисах невозможна.Еще хуже:в зависимости от реализации/конфигурации веб-службы данные byte[] могут быть преобразованы в Base64, а не в тег CDATA, и запрос может стать еще более раздутым.

P.S.:Да, как писали другие, "чукинг" возможен.Но это не стриминг как таковой ;-) - в любом случае это может вам помочь.

Не хочу рассказывать об этом тем из вас, кто считает, что потоковая веб-служба невозможна, но на самом деле все http-запросы основаны на потоках.Каждый браузер, выполняющий GET на веб-сайт, основан на потоке.Каждый вызов веб-службы основан на потоке.Да все.Мы не замечаем этого на уровне реализации сервисов или страниц, потому что за вас этим занимаются более низкие уровни архитектуры, но это делается.

Вы когда-нибудь замечали в браузере, что иногда загрузка страницы может занять некоторое время — браузер просто продолжает работать, показывая песочные часы?Это потому, что браузер ожидает потока.

Потоки — это причина, по которой mime/типы должны отправляться перед фактическими данными — это всего лишь поток байтов для браузера, он не сможет идентифицировать фотографию, если вы не сообщите ему, что это было первым.Именно поэтому вам необходимо передать размер двоичного файла перед отправкой — браузер не сможет определить, где изображение останавливается и страница возобновляется.

Это всего лишь поток байтов для клиента.Если вы хотите убедиться в этом сами, просто возьмите выходной поток в любой момент обработки запроса и закройте() его.Ты взорвешь все.Браузер немедленно перестанет показывать песочные часы и отобразит сообщение «невозможно найти», «сброс соединения на сервере» или другое подобное сообщение.

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

Удачи и счастливого развития – расслабьте плечи!

Я думаю, что для WCF можно определить член сообщения как поток и соответствующим образом установить привязку - я видел эту работу с wcf, разговаривающим с веб-службой Java.

Вам необходимо установить TransferMode="StreamedResponse" в конфигурации httpTransport и использовать mtomMessageEncoding (необходимо использовать специальный раздел привязки в конфигурации).

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

Апач CXF поддерживает отправку и получение потоков.

Один из способов сделать это — добавить загрузитьFileChunk(byte[] chunkData, int size, int offset, int totalSize) метод (или что-то в этом роде), который загружает части файла, а серверы записывают их на диск.

Имейте в виду, что запрос веб-сервиса по сути сводится к одному HTTP POST.

Если вы посмотрите на выходные данные файла .ASMX в .NET, вы увидите, как именно будут выглядеть запрос и ответ POST.

Как упоминал @Guvante, дробление будет наиболее близким к тому, что вы хотите.

Я полагаю, вы могли бы реализовать свой собственный код веб-клиента для обработки TCP/IP и потоковой передачи данных в ваше приложение, но это было бы, по меньшей мере, сложно.

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

Например, вы можете использовать Коммонс библиотека с открытым исходным кодом.

А РМИИО Библиотека для Java обеспечивает передачу RemoteInputStream через RMI — нам нужен был только RMI, хотя у вас должна быть возможность адаптировать код для работы с другими типами RMI.Это может вам помочь, особенно если у вас есть небольшое приложение на стороне пользователя.Библиотека была разработана специально для того, чтобы иметь возможность ограничить размер данных, передаваемых на сервер, чтобы избежать именно той ситуации, которую вы описываете - фактически атаки DOS путем заполнения оперативной памяти или диска.

С помощью библиотеки RMIIO серверная часть решает, какой объем данных она готова получить, а с помощью HTTP PUT и POST клиент может принять это решение, включая скорость, с которой он отправляет данные.

Да, веб-сервис может выполнять потоковую передачу.Я создал веб-сервис с использованием Apache Axis2 и MTOM для поддержки рендеринга PDF-документов из XML.Поскольку полученные файлы могли быть довольно большими, потоковая передача была важна, поскольку мы не хотели хранить все это в памяти.Взгляните на документацию Oracle по потоковая передача вложений SOAP.

Альтернативно вы можете сделать это самостоятельно, и tomcat создаст заголовки Chunked.Это пример функции контроллера Spring, которая выполняет потоковую передачу.

 @RequestMapping(value = "/stream")
        public void hellostreamer(HttpServletRequest request, HttpServletResponse response) throws CopyStreamException, IOException  
{

            response.setContentType("text/xml");
            OutputStreamWriter writer = new OutputStreamWriter (response.getOutputStream());
            writer.write("this is streaming");
            writer.close();

    }

На самом деле не так уж и сложно «обрабатывать TCP/IP и передавать данные в ваше приложение».Попробуй это...

class MyServlet extends HttpServlet
{
    public void doGet(HttpServletRequest request, HttpServletResponse response)
    {
        response.getOutputStream().println("Hello World!");
    }
}

И это все, что нужно сделать.В приведенном выше коде вы ответили на HTTP-запрос GET, отправленный из браузера, и вернули этому браузеру текст «Hello World!».

Имейте в виду, что "Привет, мир!" не является действительным HTML, поэтому вы можете получить ошибку в браузере, но это действительно все, что нужно.

Удачи в вашем развитии!

Родни

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top