Подтвердить что ты не робот

OutputStream OutOfMemoryError при отправке HTTP

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

вот код

public boolean publishFile(URI publishTo, String localPath) throws Exception {
    InputStream istream = null;
    OutputStream ostream = null;
    boolean isPublishSuccess = false;

    URL url = makeURL(publishTo.getHost(), this.port, publishTo.getPath());
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();


    if (conn != null) {

        try {

            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("PUT");
            istream = new FileInputStream(localPath);
            ostream = conn.getOutputStream();

            int n;
            byte[] buf = new byte[4096];
            while ((n = istream.read(buf, 0, buf.length)) > 0) {
                ostream.write(buf, 0, n); //<--- ERROR happens on this line.......???
            }

            int rc = conn.getResponseCode();

            if (rc == 201) {
                isPublishSuccess = true;
            }

        } catch (Exception ex) {
            log.error(ex);
        } finally {
            if (ostream != null) {
                ostream.close();
            }

            if (istream != null) {
                istream.close();
            }
        }
    }

    return isPublishSuccess;

}

HEre - это ошибка, которую я получаю...

Exception in thread "Thread-8773" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2786)
    at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
    at sun.net.www.http.PosterOutputStream.write(PosterOutputStream.java:61)
    at com.test.HTTPClient.publishFile(HTTPClient.java:110)
    at com.test.HttpFileTransport.put(HttpFileTransport.java:97)
4b9b3361

Ответ 1

HttpUrlConnection буферизует данные, чтобы он мог установить заголовок Content-Length (за спецификация HTTP).

Один из вариантов, если ваш целевой сервер поддерживает его, заключается в использовании " chunked". Это будет задерживать только небольшую часть данных за раз. Однако не все службы поддерживают его (например, Amazon S3).

Другой альтернативный вариант (и imo лучше) - использовать Jakarta HttpClient. Вы можете установить "сущность" в запросе из файла, а код подключения правильно настроит заголовки запросов.


Изменить: nos прокомментировал, что OP может вызвать HttpURLConnection.setFixedLengthStreamingMode(long length). Я не знал об этом методе; он был добавлен в 1.5, и я не использовал этот класс с тех пор.

Однако я все же предлагаю использовать Jakarta HttpClient по той простой причине, что он уменьшает количество кода, который должен поддерживать OP. Код, который является шаблоном, но все еще имеет потенциал для ошибок:

  • OP правильно обрабатывает цикл для копирования между входом и выходом. Обычно, когда я вижу пример этого, плакат либо неправильно проверяет размер возвращаемого буфера, либо сохраняет повторное выделение буферов. Поздравляем, но теперь вы должны следить за тем, чтобы ваши преемники проявляли большую заботу.
  • Обработка исключений не так хороша. Да, OP запоминает, чтобы закрыть соединения в блоке finally, и снова, поздравляю с этим. За исключением того, что любой из вызовов close() мог бы выбросить IOException, оставив другого в выполнении. И метод в целом выбрасывает Exception, поэтому компилятор не собирается улавливать подобные ошибки.
  • Я подсчитываю 31 строку кода для настройки и выполнения ответа (исключая проверку кода ответа и вычисление URL-адреса, но включая try/catch/finally). С HttpClient это будет где-то в диапазоне от полудюжины LOC.

Даже если OP написал этот код отлично и реорганизовал его в методы, аналогичные методам в Jakarta Commons IO, он/она не должны этого делать. Этот код был написан и проверен другими. Я знаю, что это пустая трата времени, чтобы переписать ее, и подозревать, что это тоже трата времени OP.

Ответ 2

conn.setFixedLengthStreamingMode((int) new File(localpath).length());

И для буферизации вы можете покрыть свои потоки в BufferedOutputStream и BufferedInputStream

Хороший пример размещенной загрузки вы можете найти там: gdata-java-client

Ответ 3

Проблема заключается в том, что класс HttpURLConnection использует массив байтов для хранения ваших данных. Предположительно, это видео, которое вы нажимаете, занимает больше памяти, чем доступно. У вас есть несколько вариантов:

  • Увеличьте объем памяти в приложении. Вы можете использовать опцию -Xmx1024m, чтобы предоставить 1 ГБ памяти вашему приложению. Это увеличит объем данных, которые вы можете сохранить в памяти.

  • Если у вас по-прежнему не хватает памяти, вы можете захотеть попробовать другую библиотеку, чтобы подтолкнуть видео, которое не хранит данные в памяти сразу. У Apache Commons HttpClient есть такая функция. См. Этот сайт для получения дополнительной информации: http://hc.apache.org/httpclient-3.x/features.html. См. Этот раздел для многофакторной загрузки больших файлов: http://hc.apache.org/httpclient-3.x/methods/multipartpost.html

Ответ 4

Для чего-либо другого, кроме базовых операций GET, встроенный HTTP-материал java.net не очень хорош. Для этого рекомендуется использовать Apache Commons HttpClient. Это позволяет вам делать гораздо более интуитивно понятный материал, например:

PutMethod put = new PutMethod(url);
put.setRequestEntity(new FileRequestEntity(localFile, contentType));
int responseCode = put.executeMethod();

который заменяет много кода вашей котельной.

Ответ 5

HttpsURLConnection # setChunkedStreamingMode (1024 * 1024 * 10);//10 МБ кусок Это гарантирует, что любой файл (любого размера) передается по каналу https без внутренней буферизации. Это следует использовать, когда размер файла или длина содержимого неизвестны.

Ответ 6

Ваша проблема заключается в том, что вы пытаетесь исправить X-байты в X/N байтах ОЗУ, когда N > 1.

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

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