29

When I want to write the full contents of a file into an OutputStream, I usually allocate a buffer as a byte[], then make a for loop to read data from the file's InputStream into the buffer and write the buffer contents into the OutputStream, until the InputStream has no more bytes available.

This seems rather clumsy to me. Is there a better way to do this?

Also, I am always unsure about the buffer size. Usually, I am allocating 1024 bytes, because it just feels good. Is there a better way to determine a reasonable buffer size?

In my current case, I want to copy the full contents of a file into the output stream that writes the contents of an HTTP response. So, this is not a question about how to copy files on the file system.

Madoc
  • 5,841
  • 4
  • 25
  • 38

3 Answers3

42

For Java 1.7+ you can use the Files.copy(Path, OutputStream), e.g.

HttpServletResponse response = // ...
File toBeCopied = // ...

try (OutputStream out = response.getOutputStream()) {
    Path path = toBeCopied.toPath();
    Files.copy(path, out);
    out.flush();
} catch (IOException e) {
    // handle exception
}

Note, since you are dealing with HttpServletResponse is is also a good idea to set correct response headers. Add the following lines before you copy the actual file data to the response:

String mimeType = URLConnection.guessContentTypeFromName(toBeCopied.getName());
String contentDisposition = String.format("attachment; filename=%s", toBeCopied.getName());
int fileSize = Long.valueOf(toBeCopied.length()).intValue();

response.setContentType(mimeType);
response.setHeader("Content-Disposition", contentDisposition);
response.setContentLength(fileSize);

Note, the encoding of the file name passed to the content disposition is important, see this question.

Community
  • 1
  • 1
matsev
  • 32,104
  • 16
  • 121
  • 156
  • How could my unit test verify what's being written to the OutputStream? ServletOutputStream mockOutput = mock(ServletOutputStream.class); when(response.getOutputStream()).thenReturn(mockOutput); – Philip Rego Oct 20 '17 at 22:05
30

Apache Commons-IO:

IOUtils.copy(fileInputStream,outputStream);

JDK NIO

new FileInputStream(file).getChannel().transferTo(otherChannel);
mhaller
  • 14,122
  • 1
  • 42
  • 61
  • Wow. I gave a short comment regarding Commons-IO at Bozho's answer. But the channel solution is exactly what I was looking for. Thanks a lot, all of NIO channels passed me by, because I had already learned Java IO before NIO existed. And, by the way: 9 minutes! That's almost as fast as lightning. – Madoc Jan 19 '11 at 22:35
  • 2
    don't forget to close the `FileInputStream` – yegor256 Jun 29 '12 at 11:46
12

With commons-io you have a one-line solution:

IOUtils.copy(yourFileInputStream, outputStream);

Note that you'd have to close your streams manually (or by IOUtils.closeQuitely(..))

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • This would be my suggestion as well. Though what it does is the same thing. – rfeak Jan 19 '11 at 22:25
  • That looks really nice. But for this simple task, it seems a little overdone to me to include another library. Of course, if there are more reasons to include Commons-IO, then this will be the solution of choice. – Madoc Jan 19 '11 at 22:33