Запись InputStream в HttpServletResponse

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

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);

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

Ответ 1

Просто напишите в блоках, а не сначала скопируйте его в память 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());

Ответ 2

BufferedInputStream in = null;
BufferedOutputStream out = null;
OutputStream os;
os = new BufferedOutputStream(response.getOutputStream());
in = new BufferedInputStream(new FileInputStream(file));
out = new BufferedOutputStream(os);
byte[] buffer = new byte[1024 * 8];
int j = -1;
while ((j = in.read(buffer)) != -1) {
    out.write(buffer, 0, j);
}

Ответ 3

Я думаю, что это очень близко к лучшему, но я бы предложил следующее изменение. Используйте буфер фиксированного размера (скажем, 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: ваша программа не всегда будет работать как есть, потому что read не всегда заполняет весь массив, который вы ей предоставляете.