Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to generate a dynamic InputStream in Java?

I looked around online at InputStreams and it seems that you can only define an InputStream with a pre-defined input that it will read from. What if I wanted an InputStream where we're not sure what the data looks like or how long it is but it's generated somewhere on the fly but when it's done will return zero bytes in the end?

What I mean by pre-defined is that most constructors take in something you've already defined to read from (ex: ByteArrayInputStream(byte[] array), FileInputStream(File file))

My use case here is that I have a method that returns a certain amount of data (this methods calls the service an unknown amount of time and I want to stream this data as it's being generated on the fly)

like image 472
Vicky Avatar asked Feb 26 '18 01:02

Vicky


People also ask

Can we convert OutputStream to InputStream in Java?

Though you cannot convert an OutputStream to an InputStream, java provides a way using PipedOutputStream and PipedInputStream that you can have data written to a PipedOutputStream to become available through an associated PipedInputStream.

What is the difference between InputStream and FileInputStream?

There is no real difference. FileInputStream extends InputStream , and so you can assign an InputStream object to be a FileInputStream object. In the end, it's the same object, so the same operations will happen. This behavior is called Polymorphism and is very important in Object-Oriented Programming.

Is it possible to create a file object from InputStream?

Since Java 7, you can do it in one line even without using any external libraries: Files. copy(inputStream, outputPath, StandardCopyOption.


2 Answers

Sure, just extend InputStream and have it do whatever you wish. ByteArrayInputStream does exactly this.

like image 56
user207421 Avatar answered Sep 20 '22 01:09

user207421


The InputStream class is an abstract class. It contains definitions for all relevant methods, except for one:

public int read() throws IOException

The documentation says:

Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255. If no byte is available because the end of the stream has been reached, the value -1 is returned.

So an example showing how to create an input stream from an "arbitrary" data source could look like this:

import java.io.IOException;
import java.io.InputStream;
import java.util.function.IntSupplier;

public class DynamicInputStreamExample
{
    public static void main(String[] args) throws IOException
    {
        IntSupplier supplier = () -> methodThatProvidesTheData();
        InputStream inputStream = createStream(supplier);

        while (true)
        {
            int read = inputStream.read();
            System.out.println("Read " + read + " from stream");
            if (read == -1)
            {
                break;
            }
        }
    }

    private static InputStream createStream(IntSupplier supplier)
    {
        return new InputStream()
        {
            @Override
            public int read() throws IOException
            {
                return supplier.getAsInt();
            }
        };
    }

    // Dummy implementation of a method that provides the data,
    // as a sequence of 6 bytes. It returns -1 if no more data
    // is available.
    private static final int data[] = { 'H', 'e', 'l', 'l', 'o' };
    private static int index = 0;
    private static int methodThatProvidesTheData()
    {
        if (index >= data.length)
        {
            return -1;
        }
        return data[index++];
    }

}

Note: Depending on how your data is generated, it may be beneficial to additionally override other methods of the InputStream class. Particularly, the read(byte[] b, int off, int len) method that reads an array of bytes from the source. The main benefit of this would be that your could achieve a (much) higher performance when reading multiple bytes at once. But when this is not relevant for you, then just overriding the int read() method is sufficient.

like image 31
Marco13 Avatar answered Sep 19 '22 01:09

Marco13