5

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)

Vicky
  • 113
  • 2
  • 6
  • It might be easier to write bytes to PipedOutputStream which would be read from a corresponding PipedOutputStream: https://stackoverflow.com/a/23874232/1941359 – AlexO Sep 14 '21 at 15:19

2 Answers2

4

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.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Marco13
  • 53,703
  • 9
  • 80
  • 159
3

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

user207421
  • 305,947
  • 44
  • 307
  • 483