27

I have an InputStream that I want written to a HttpServletResponse. There's this approach, which takes too long due to the use of 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);

I was wondering what could possibly be the best way to do it, in terms of speed and efficiency.

eugenevd
85810 silver badges21 bronze badges
asked Apr 13, 2012 at 14:09

3 Answers 3

54

Just write in blocks instead of copying it entirely into Java's memory first. The below basic example writes it in blocks of 10KB. This way you end up with a consistent memory usage of only 10KB instead of the complete content length. Also the enduser will start getting parts of the content much sooner.

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

As creme de la creme with regard to performance, you could use NIO Channels and a directly allocated ByteBuffer. Create the following utility/helper method in some custom utility class, e.g. 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;
 }
}

Which you then use as below:

response.setContentLength(getContentLength());
Utils.stream(getInputStream(), response.getOutputStream());
answered Apr 13, 2012 at 14:21

5 Comments

Of course many utility packages have this method already defined, so once you start using Guava... docs.guava-libraries.googlecode.com/git/javadoc/com/google/…, java.io.OutputStream)
+1 @BalusC Do we need to set its ContentType? If yes, what would it be?
@Roylee: yes, that's recommended. Just set the content type to the type of the content :) freeformatter.com/mime-types-list.html
i tried this,, why is that my stream will return an error like this: org.apache.catalina.connector.ClientAbortException: java.io.IOException: An established connection was aborted by the software in your host machine at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:356) at org.apache.catalina.connector.OutputBuffer.appendByteArray(OutputBuffer.java:778) at org.apache.catalina.connector.OutputBuffer.append(OutputBuffer.java:707) at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:391)
how to getContentLenght() of input Stream?
1
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);
}
answered Apr 13, 2012 at 14:18

3 Comments

I wan to avoid the usage of byte[], if possible
@MuhammadSabry there's no way to read the InputStream w/o using byte[]
What's wrong with byte[]. You need to store the data someware :}
0

I think that is very close to the best way, but I would suggest the following change. Use a fixed size buffer(Say 20K) and then do the read/write in a loop.

For the loop do something like

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: Your program will not always work as is, because read don't always fill up the entire array you give it.

Luatic
11.3k3 gold badges19 silver badges40 bronze badges
answered Apr 13, 2012 at 14:20

2 Comments

what exactly do you mean by read doesn't fill up the entire array you give it?
Read does not always fill up the input array. So you need to check the value which read return which is the number of bytes read. (See docs.oracle.com/javase/6/docs/api/java/io/…)

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.