Take this simple test class:
public class InputStreamTest {
public static void main(String[] args) throws IOException {
byte[] buf = new byte[100];
Path path = Paths.get(args[0]);
System.out.println("File " + path);
try (InputStream is = new BufferedInputStream(new FileInputStream(path.toFile()))) {
// try (InputStream is = new BufferedInputStream(Files.newInputStream(path))) {
System.out.println("Available: " + is.available());
System.out.print("First 100 bytes: '");
is.read(buf);
System.out.write(buf);
System.out.println("'");
}
}
}
This takes a filename/path on the command line and simply prints out the result of available()
on the corresponding InputStream
and up to1 the first 100 bytes from the stream.
It works fine, even when you pass it redirected command output (which internally will create a FIFO and pass it as the file argument). For example, running it as:
java InputStreamTest <(echo -n "ABC")
... results in the following output:
File /dev/fd/63:
Available: 3
First 100 bytes: 'ABC'
Looks good so far. Now, uncomment the line that uses the new-and-recommended-Java-7-ish Files.newInputStream(path)
to create the InputStream
and comment our the prior line which uses the oldschool FileInputStream
class. Now it fails:
File /dev/fd/63
Exception in thread "main" java.io.IOException: Illegal seek
at sun.nio.ch.FileChannelImpl.position0(Native Method)
at sun.nio.ch.FileChannelImpl.position(FileChannelImpl.java:264)
at sun.nio.ch.ChannelInputStream.available(ChannelInputStream.java:116)
at java.io.BufferedInputStream.available(BufferedInputStream.java:410)
at net.tdowns.compression.InputStreamTest.main(InputStreamTest.java:20)
Evidently the available()
method on the stream is failing. Now you might just say "hey, don't call available()
it is pretty much useless". Unfortunately, a class as core as BufferedInputStream
uses it! If you comment out the direct call to available()
, this still fails:
File /dev/fd/63
First 100 bytes: 'Exception in thread "main" java.io.IOException: Illegal seek
at sun.nio.ch.FileChannelImpl.position0(Native Method)
at sun.nio.ch.FileChannelImpl.position(FileChannelImpl.java:264)
at sun.nio.ch.ChannelInputStream.available(ChannelInputStream.java:116)
at java.io.BufferedInputStream.read(BufferedInputStream.java:353)
at java.io.FilterInputStream.read(FilterInputStream.java:107)
at net.tdowns.compression.InputStreamTest.main(InputStreamTest.java:22)
So you are in a pretty tough spot - if you use Files.newInputStream(path)
, the available()
call might fail, and you can't really avoid use of this call since other JDK stream classes themselves like to call it.
Any way out?
1 I say up to because of course the read()
method may return fewer than the requested number of bytes, even if more are available. In practice, it doesn't do that for files and for fifos you get at least a few K, so this will usually print out 100 bytes if 100 bytes are available.