2

As I understand, an InputStream is a stream of bytes. I am interested in converting an InputStream object to a stream of bytes. Basically, an implementation of the following method.

public Stream<byte[]> toStream(final InputStream is, final int bufferSize); 

What would be the best way to get this done? The buffer size is the number of bytes read from the InputStream at a time.

12
  • How are you currently reading/processing your InputStream? Commented Nov 7, 2019 at 8:04
  • Why do you want a Stream<byte[]>? How are you planning on processing it? Commented Nov 7, 2019 at 8:07
  • 3
    @thisisshantzz then you're looking at the wrong approach. You don't need a Stream, you need to connect the InputStream to the client's OutputStream, and just stream bytes normally. How this is done specifically depends on your environment, but for example with Spring you could use a StreamingResponseBody. Commented Nov 7, 2019 at 8:22
  • 1
    In the signature of the method you have posted, you are trying to convert an InputStream to a Stream of Array of bytes not Stream of bytes. First of all you have to explain what is the meaning of that array of bytes. If they are text lines you can use this stackoverflow.com/questions/30336257/…. If you want a stream of bytes instead of stream of array of bytes you can use the readAllBytes method of the inputStream and then convert the array to a Stream Commented Nov 7, 2019 at 8:24
  • 1
    @thisisshantzz if you just want to "redirect" the InputStream to the client, then any additional Flux or byte[] in between is completely unnecessary. It's just normal basic streaming. See here for more info. Commented Nov 7, 2019 at 8:28

1 Answer 1

2

You need to write your own Spliterator, something like this:

public final class ChunkingInputStreamSpliterator implements Spliterator<byte[]> { private final InputStream is; private final int bufferSize; public ChunkingInputStreamSpliterator(InputStream is, int bufferSize) { this.is = is; this.bufferSize = bufferSize; } @Override public boolean tryAdvance(Consumer<? super byte[]> action) { byte[] bytes; try { bytes = this.is.readNBytes(this.bufferSize); } catch (IOException e) { throw new UncheckedIOException(e); } if (bytes.length == 0) return false; action.accept(bytes); return true; } @Override public Spliterator<byte[]> trySplit() { return null; // cannot split an InputStream } @Override public long estimateSize() { return Long.MAX_VALUE; // unknown } @Override public int characteristics() { return Spliterator.ORDERED | Spliterator.NONNULL; } } 

Then implement your method like this:

public static Stream<byte[]> toStream(InputStream is, int bufferSize) { return StreamSupport.stream(new ChunkingInputStreamSpliterator(is, bufferSize), false); } 

If you don't have Java 11, so you don't have the very convenient readNBytes method, then do that part yourself like this:

public boolean tryAdvance(Consumer<? super byte[]> action) { byte[] bytes = new byte[this.bufferSize]; int len = 0; try { for (int read; len < bytes.length; len += read) if ((read = this.is.read(bytes, len, bytes.length - len)) <= 0) break; } catch (IOException e) { throw new UncheckedIOException(e); } if (len == 0) return false; if (len < bytes.length) bytes = Arrays.copyOfRange(bytes, 0, len); action.accept(bytes); return true; } 
Sign up to request clarification or add additional context in comments.

2 Comments

Would it be wise to replace the Long.MAX_VALUE by this.is.available()/this.bufferSize in the estimatedSize method of the Spliterator?
@thisisshantzz if you know the size, e.g. because it’s a file, you should provide the size upfront (though, in that case, a FileChannel would be way more efficient). Otherwise, InputStream.available() does not provide the correct size. It only tells how much can be read without blocking.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.