Explain Codes LogoExplain Codes Logo

Easy way to write contents of a Java InputStream to an OutputStream

java
io-operations
stream-management
best-practices
Alex KataevbyAlex Kataev·Feb 6, 2025
TLDR
// Using Java NIO's Files.copy utility to transfer data between streams import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; public static void copyStream(InputStream input, OutputStream output) throws IOException { Files.copy(input, output); // You shall copy! }

Leverage Java's Files.copy() method for a clean and efficient way to copy an InputStream to an OutputStream.

Transfer content with Java 9's transferTo

Java 9 introduced a simpler and direct method to transfer content from InputStream to OutputStream, known as transferTo:

inputStream.transferTo(outputStream); // Skydiving data from one stream to the other.

Don't forget that transferTo doesn't close the streams automatically. You should do it!

Clean up after yourself: Close the streams

Resource management is no joke. Always close your streams:

try (InputStream in = ...; OutputStream out = ...) { in.transferTo(out); // Sharing is caring! }

This construct ensures the streams are closed, keeping your codebase clean and leak-free.

Being the 'catch' with exception handling

Exceptions like IOException need to be handled:

try { copyStream(sourceInputStream, targetOutputStream); // Just do it! } catch (IOException e) { // Handle exception like a boss }

This ensures your I/O operations are robust and tells other devs you've got it under control.

Using enhanced tools: Apache and Guava

Apache Commons IO IOUtils.copy() and Google's Guava ByteStreams.copy() are your allies:

// Using Apache Commons IO IOUtils.copy(inputStream, outputStream); // Call me Copycat! // Using Guava ByteStreams.copy(inputStream, outputStream); //-The Guava-nator

These utilities handle buffering, ensure efficient data transfer, and keep the code readable.

When the going gets tough: Manual copying

Manual copying gives you control over the buffer size:

byte[] buffer = new byte[8192]; // Buffer: the sponge of data int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); // Work, work! }

Remember, tuning depends on your application's specific needs.

The flow of streams: Visualization

InputStream 🚰 ====(💧)====> OutputStream

Each symbol depicts:

  • 🚰: The inlet pipe - InputStream
  • 💧: Data flow
  • ===>: The read() and write() operations
  • OutputStream: The pipe's outlet - the data's final destination

Data flows as smoothly as water through a pipe.

Streams: know their secrets

Streams can block indefinitely if the InputStream has no available data. Be ready to handle these situations.

Dealing with file systems

Files.copy is great for file-to-file transfers and supports options like StandardCopyOption.REPLACE_EXISTING:

Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); // Wooosh!

Balancing act: clarity and performance

Balancing code readability with performance is an art. Remember, self-explanatory code is a gift you give to future you or other developers.

Testing your methods

Make sure you include tests to verify the functionality of your stream transfer methods. Remember, the only good code is tested code.