8

I have a function, int readFully(FileHandle handle, OutputStream out), which reads in an entire file from an SSH server and stores it in a local stream. I can write the file locally by using a FileOutputStream for the second parameter and then read that file into a BufferedImage using something like this:

bufferedImage = ImageIO.read(new File("/path/to/file"));

But how can I create the bufferedImage directly without first writing it to a file? I looked at this question, but still can't figure it out for my case.

Community
  • 1
  • 1
gogators
  • 783
  • 2
  • 6
  • 17
  • 3
    What is this `FileHandle` class in the parameter? If you can get an `InputStream` from it, then you can use `ImageIO.read(InputStream input)` method. – Aleks G Jul 03 '13 at 15:26
  • This may help: http://stackoverflow.com/questions/364936/input-and-output-stream-pipe-in-java – PeterMmm Jul 03 '13 at 15:26
  • The `FileHandle` class holds some basic properties of the file, e.g. filename, path, permissions, etc. It doesn't have much functionality and I can't get an InputStream from it. – gogators Jul 03 '13 at 15:35

3 Answers3

15

Just write to a ByteArrayOutputStream instead - you can then create a ByteArrayInputStream to read from the same byte array, and then pass that to ImageIO.read(InputStream).

ByteArrayOutputStream output = new ByteArrayOutputStream();
readFully(handle, output);
byte[] data = output.toByteArray();
ByteArrayInputStream input = new ByteArrayInputStream(data);
BufferedImage image = ImageIO.read(input);

That's assuming you can't actually create an InputStream directly from the FileHandle, which would be even simpler.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Can you explain how to "create a `ByteArrayInputStream` to read from the same byte array"? Some example code would help. Thanks. – gogators Jul 03 '13 at 15:54
  • Thanks. That works. Not sure who's answer to accept. I guess @JonSkeet's since his was first, but ssssteffff provided the details first. – gogators Jul 03 '13 at 16:02
  • No worry ;) I hesitated between Piped and ByteArray streams, I saw @JonSkeet answer so I mentioned Piped ones in my initial answer. Thanks to him, he pointed out a real issue before I tried to find why for hours, so he deserves it! :p – ssssteffff Jul 03 '13 at 16:11
3

You could use a PipedInputStream and a PipedOutputStream like this:

PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);

readFully(handle, pos);

ImageIO.read(pis);

The piped output/input streams are linked, so what is "written" to pos will be readable from pis.

EDIT

Jon pointed out that the documentation states

Attempting to use both objects from a single thread is not recommended as it may deadlock the thread.

You should not use this within the same thread. If you want a single thread code, you could use ByteArrayOutputStream / ByteArrayInputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
readFully(handle, baos);

ImageIO.read(new ByteArrayInputStream(baos.toByteArray());

All these classes (PipedInputStream, PipedOutputStream, ByteArrayInputStream, ByteArrayOutputStream) are present since JDK 1.0.

ssssteffff
  • 964
  • 4
  • 16
  • Nice - I always forget about those classes (hardly having used them myself). On the other hand, the documentation states: "Attempting to use both objects from a single thread is not recommended as it may deadlock the thread." Presumably there's a buffer within the stream... but I can't see a way of setting its size. (It would have to be big enough for the whole image.) – Jon Skeet Jul 03 '13 at 15:40
  • I tried this, but it hangs at the call to `readFully`. Perhaps due to the reasons Jon Skeet mentioned. – gogators Jul 03 '13 at 15:52
  • @KevinVW It's totally due to JonSkeet comment, I edited my answer. – ssssteffff Jul 03 '13 at 15:59
0

Because piped I/O is such a good idea, but has a thread issue, the following theft from @ssssteffff.

    try {
        final PipedOutputStream out = new PipedOutputStream();
        PipedInputStream in = new PipedInputStream(out);

        new Thread(){

            @Override
            public void run() {
                try {
                    readFully(handle, out);
                    out.close();
                } catch (IOException ex) {
                    Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

        }.start();

        BufferedImage img = ImageIO.read(in);
        in.close();

        ...            
    } catch (IOException ex) {
        Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
    }
  • Written is to out in a new thread.
  • The piped input in is connected to out, waiting for its data.
  • Probably more palatable in Java 8.
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138