Sunday, 7 April 2024

Copy InputStream to OutputStream in Java

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

Get the hash or message digest of a file in Java

No comments:

Post a Comment