Запишите InputStream в HttpServletResponse

У меня есть InputStream, который я хочу записать в HttpServletResponse. Есть такой подход, который занимает слишком много времени из-за использования byte[]

InputStream is = getInputStream();
int contentLength = getContentLength();

byte[] data = new byte[contentLength];
is.read(data);

//response here is the HttpServletResponse object
response.setContentLength(contentLength);
response.write(data);

Мне было интересно, что может быть лучшим способом сделать это с точки зрения скорости и эффективности.


person Sabry Shawally    schedule 13.04.2012    source источник


Ответы (3)


Просто пишите блоками, а не копируйте их целиком в память Java. В приведенном ниже базовом примере он записывается блоками по 10 КБ. Таким образом, вы получите последовательное использование памяти всего в 10 КБ вместо полной длины содержимого. Кроме того, конечный пользователь начнет получать части контента намного раньше.

response.setContentLength(getContentLength());
byte[] buffer = new byte[10240];

try (
    InputStream input = getInputStream();
    OutputStream output = response.getOutputStream();
) {
    for (int length = 0; (length = input.read(buffer)) > 0;) {
        output.write(buffer, 0, length);
    }
}

В качестве сливок производительности вы можете использовать NIO Channels и непосредственно размещенный ByteBuffer . Создайте следующий служебный/вспомогательный метод в некотором пользовательском служебном классе, например. Utils:

public static long stream(InputStream input, OutputStream output) throws IOException {
    try (
        ReadableByteChannel inputChannel = Channels.newChannel(input);
        WritableByteChannel outputChannel = Channels.newChannel(output);
    ) {
        ByteBuffer buffer = ByteBuffer.allocateDirect(10240);
        long size = 0;

        while (inputChannel.read(buffer) != -1) {
            buffer.flip();
            size += outputChannel.write(buffer);
            buffer.clear();
        }

        return size;
    }
}

Который вы затем используете, как показано ниже:

response.setContentLength(getContentLength());
Utils.stream(getInputStream(), response.getOutputStream());
person BalusC    schedule 13.04.2012
comment
+1 @BalusC Нужно ли нам устанавливать его ContentType? Если да, то что это будет? - person RLEE; 19.03.2013
comment
@Roylee: да, это рекомендуется. Просто установите тип контента в соответствии с типом контента :) freeformatter.com/mime-types -list.html - person BalusC; 19.03.2013
comment
я попробовал это, почему мой поток вернет такую ​​​​ошибку: org.apache.catalina.connector.ClientAbortException: java.io.IOException: установленное соединение было прервано программным обеспечением на вашем хост-компьютере в org.apache. catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:356) в org.apache.catalina.connector.OutputBuffer.appendByteArray(OutputBuffer.java:778) в org.apache.catalina.connector.OutputBuffer.append(OutputBuffer.java: 707) в org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:391) - person WantIt; 14.05.2017
comment
как получитьContentLenght() входного потока? - person Pramod Gaikwad; 30.11.2017

Я думаю, что это очень близко к лучшему, но я бы предложил следующее изменение. Используйте буфер фиксированного размера (скажем, 20 КБ), а затем выполните чтение/запись в цикле.

Для цикла сделайте что-то вроде

byte[] buffer=new byte[20*1024];
outputStream=response.getOutputStream();
while(true) {
  int readSize=is.read(buffer);
  if(readSize==-1)
    break;
  outputStream.write(buffer,0,readSize);
}

ps: ваша программа не всегда будет работать как есть, потому что чтение не всегда заполняет весь массив, который вы ей даете.

person MTilsted    schedule 13.04.2012
comment
что именно вы подразумеваете под чтением, не заполняет весь массив, который вы ему даете? - person Sabry Shawally; 13.04.2012
comment
Чтение не всегда заполняет входной массив. Поэтому вам нужно проверить значение, которое считывается, которое является количеством прочитанных байтов. (См. документы. oracle.com/javase/6/docs/api/java/io/) - person MTilsted; 13.04.2012

person    schedule
comment
Я хочу избежать использования byte[], если это возможно - person Sabry Shawally; 13.04.2012
comment
@MuhammadSabry нет возможности прочитать InputStream без использования byte[] - person Luiggi Mendoza; 13.04.2012
comment
Что не так с byte[]. Вам нужно как-то хранить данные :} - person MTilsted; 13.04.2012