In Java, copying data from an InputStream to an OutputStream is a common task, especially when dealing with file operations, network communications, or handling HTTP requests. In this blog post, we'll explore efficient methods to perform this operation, discussing best practices and potential pitfalls along the way.
Below are several scenarios in which copying an InputStream to an OutputStream is common in Java:
1. Data Forwarding: In situations where you need to transfer data from one stream to another, such as redirecting the output of one program to serve as input for another.
2. File Persistence: When dealing with an InputStream sourced from a network request or another origin, you may want to replicate its content into a FileOutputStream to persist the data into a file.
3. Temporary Data Storage: Utilizing a ByteArrayOutputStream, you can duplicate a stream's content into memory temporarily, facilitating subsequent processing operations.
Approach 1: Using plain core java InputStream and OutputStream classes.
IOStreamUtil1.java
package com.sample.app.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class IOStreamUtil1 {
private IOStreamUtil1() {
// Restrict to create an object to this
}
public static final int DEFAULT_BUFFER_SIZE = 8192;
public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream)
throws IOException {
return copy(inputStream, outputStream, closeOutputStream, new byte[DEFAULT_BUFFER_SIZE]);
}
public static long copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream,
byte[] buffer) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream is null");
}
if (outputStream == null) {
throw new IllegalArgumentException("outputStream is null");
}
try (InputStream in = inputStream) {
long total = 0;
int res = -1;
while ((res = in.read(buffer)) != -1) {
total += res;
if (outputStream != null) {
outputStream.write(buffer, 0, res);
}
}
if (closeOutputStream) {
outputStream.close();
} else {
outputStream.flush();
}
return total;
}
}
public static void main(String[] args) throws IOException {
// Create sample input and output streams
ByteArrayInputStream inputStream = new ByteArrayInputStream("Hello, World!".getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Test the copy method
long bytesCopied = copy(inputStream, outputStream, true);
// Print the result
System.out.println("Bytes copied: " + bytesCopied);
System.out.println("Output stream content: " + outputStream.toString());
}
}
Output
Bytes copied: 13 Output stream content: Hello, World!
Approach 2: Using InputStream#transferTo method.
public long transferTo(OutputStream out) throws IOException
This method is introduced in Java9. Reads all bytes from this input stream and writes the bytes to the given output stream in the order that they are read. On return, this input stream will be at end of stream. This method does not close either stream. If the total number of bytes transferred is greater than Long.MAX_VALUE, then Long.MAX_VALUE will be returned.
IOStreamUtil2.java
package com.sample.app.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class IOStreamUtil2 {
public static long copy(InputStream inputStream, OutputStream outputStream) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream is null");
}
if (outputStream == null) {
throw new IllegalArgumentException("outputStream is null");
}
return inputStream.transferTo(outputStream);
}
public static void main(String[] args) throws IOException {
// Create sample input and output streams
ByteArrayInputStream inputStream = new ByteArrayInputStream("Hello, World!".getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Test the copy method
long bytesCopied = copy(inputStream, outputStream);
// Print the result
System.out.println("Bytes copied: " + bytesCopied);
System.out.println("Output stream content: " + outputStream.toString());
}
}
Approach 3: Using Apache commons IOUtils.copy method.
Apache Commons IO library provides utility methods for working with streams, including copying streams efficiently. Using this library simplifies the code and makes it more readable.
public static int copy(final InputStream inputStream, final OutputStream outputStream) throws IOException
Copies bytes from an InputStream to an OutputStream. Large streams (over 2GB) will return a bytes copied value of -1 after the copy has completed since the correct number of bytes cannot be returned as an int. For large streams use the copyLarge(InputStream, OutputStream) method.
IOStreamUtil3.java
package com.sample.app.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
public class IOStreamUtil2 {
public static int copy(InputStream inputStream, OutputStream outputStream) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream is null");
}
if (outputStream == null) {
throw new IllegalArgumentException("outputStream is null");
}
int noOfBytesopied = IOUtils.copy(inputStream, outputStream);
return noOfBytesopied;
}
public static void main(String[] args) throws IOException {
// Create sample input and output streams
ByteArrayInputStream inputStream = new ByteArrayInputStream("Hello, World!".getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Test the copy method
long bytesCopied = copy(inputStream, outputStream);
// Print the result
System.out.println("Bytes copied: " + bytesCopied);
System.out.println("Output stream content: " + outputStream.toString());
}
}
Approach 4: Using IOUtils.copyLarge method.
public static long copyLarge(final InputStream inputStream, final OutputStream outputStream) throws IOException
public static long copyLarge(final InputStream inputStream, final OutputStream outputStream, final byte[] buffer)
Copies bytes from a large (over 2GB) InputStream to an OutputStream This method buffers the input internally, so there is no need to use a BufferedInputStream. Default buffer size is 8192 bytes.
IOStreamUtil4.java
package com.sample.app.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
public class IOStreamUtil3 {
public static long copy(InputStream inputStream, OutputStream outputStream) throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream is null");
}
if (outputStream == null) {
throw new IllegalArgumentException("outputStream is null");
}
long noOfBytesopied = IOUtils.copyLarge(inputStream, outputStream);
return noOfBytesopied;
}
public static void main(String[] args) throws IOException {
// Create sample input and output streams
ByteArrayInputStream inputStream = new ByteArrayInputStream("Hello, World!".getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Test the copy method
long bytesCopied = copy(inputStream, outputStream);
// Print the result
System.out.println("Bytes copied: " + bytesCopied);
System.out.println("Output stream content: " + outputStream.toString());
}
}
Approach 5: Using Java NIO
Java NIO offers a non-blocking I/O API that can also be used for efficient stream copying. This approach is particularly useful when dealing with large files or when performance is critical.
IOStreamUtil5.java
package com.sample.app.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
public class IOStreamUtil4 {
public static void copy(InputStream inputStream, OutputStream outputStream, boolean closeOutputStream)
throws IOException {
if (inputStream == null) {
throw new IllegalArgumentException("inputStream is null");
}
if (outputStream == null) {
throw new IllegalArgumentException("outputStream is null");
}
try (InputStream inp = inputStream;
ReadableByteChannel inChannel = Channels.newChannel(inp);
WritableByteChannel outChannel = Channels.newChannel(outputStream)) {
ByteBuffer buffer = ByteBuffer.allocateDirect(8192); // 8KB buffer size
while (inChannel.read(buffer) != -1) {
buffer.flip();
outChannel.write(buffer);
buffer.clear();
}
}
if (closeOutputStream) {
outputStream.close();
} else {
outputStream.flush();
}
}
public static void main(String[] args) throws IOException {
// Create sample input and output streams
ByteArrayInputStream inputStream = new ByteArrayInputStream("Hello, World!".getBytes());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// Test the copy method
copy(inputStream, outputStream, true);
System.out.println("Output stream content: " + outputStream.toString());
}
}
Copying data from an InputStream to an OutputStream is a fundamental operation in Java programming. By using buffered streams, leveraging third-party libraries like Apache Commons IO, or utilizing the Java NIO package, you can perform this task efficiently and effectively. Consider your specific requirements, such as performance, resource usage, and code readability, when choosing the appropriate method for copying streams in your Java applications.
You may like
file and stream programs in Java
Write InputStream to a file in Java
File separator, separatorChar, pathSeparator, pathSeparatorChar in Java
Implement an Output stream that writes the data to two output streams
Check whether directory can be accessed and has read and write privileges in Java
No comments:
Post a Comment