-2

i am making a java program that reads data from a binary stream (using a DataInputStream).

Sometimes during this process i need to read a data chunk, however the method (which i cannot modify) that reads it will stop before reaching the end of the chunk (it is the normal behavior, apparently it just doesn't need the last bytes, but i can't do anything about the fact that they are there). This is not a problem in itself because i know exactly how long the chunk is, i.e. i know how many bytes there are in the chunk so i can skip bytes (with the skipBytes(int) method) until the end of the chunk ; the problem is : i don't actually know how many bytes the method actually read (or left), so i don't know how many bytes i need to skip to reach the end of the chunk.
Is there any way to :

  • know how many bytes were read in a stream since a certain point in time ?
  • know how many bytes were read in a stream since it was ?
  • any other way i could know how many bytes my data-chunk-reading method just read (since it won't directly tell me) ?

Just in case, i made a small diagram

Thanks in advance

TwilCynder
  • 33
  • 6
  • Are you saying that the stream source always sends *N* bytes, even though the image data may only take up a fraction of that? – VGR Feb 03 '22 at 19:28
  • Yeah, more or less. It is a binary file that contains images, and right before each image i have the length of the image ; but the last bytes of the chunk that is supposed to be the image are just ... Useless bytes, I don't know why they are there, but the image reading function doesn't read them and I need to skip them to resume reading useful data – TwilCynder Feb 03 '22 at 23:35
  • You could pass an [ImageInputStream](https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/javax/imageio/ImageIO.html#createImageInputStream(java.lang.Object)) to ImageIO.read. ImageInputStream has a `getStreamPosition()` method in addition to most of the standard InputStream methods and DataInput methods. Just make sure you do all your reading through it, and not the underlying InputStream, as mixing reads from both will lead to strange behavior. – VGR Feb 03 '22 at 23:44
  • hmm, this looks like what i need indeed. I am tempted to actually just replace my DataInputSteam with an ImageInputStream since it also has the DataInput methods, but since ImageInputStream is just an interface, i don't really know which class to use exactly. i'll try to look into that. – TwilCynder Feb 04 '22 at 00:35
  • That’s why I linked to a method for creating an ImageInputStream. You don’t need to care that it’s an interface. What’s important is that you’ll have an object, of some type internal to ImageIO, which implements ImageInputStream. – VGR Feb 04 '22 at 00:47
  • Oh yeah, so indeed i could entirely remplace my DataInputStream with a ImageInputStream (with soem refactoring since i am currently subclassing DIS) However i just ran into another problem : ImageIO.read(IIS) actually closes the stream. – TwilCynder Feb 04 '22 at 10:19

2 Answers2

0

All the buffered read methods return the actual number of bytes read.

Quoting documentation for InputStream#read(byte[] b):

Returns: the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached.

Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • The read method in question here is not InputStream#read(). It is ImageIO#read(Input stream), which does not return the number of bytes read – TwilCynder Feb 03 '22 at 23:42
0

ImageInputStream can do what you want. It implements DataInput and it has most of the methods of InputStream. And it has getStreamPosition, seek and skipBytes methods.

However, as you correctly point out, ImageIO.read(ImageInputStream) would close the stream, preventing you from reading more than one image.

The solution is to avoid using ImageIO.read, and instead obtain an ImageReader explicitly, using ImageIO.getImageReaders. Then you can invoke an ImageReader’s read method, which does not close the stream.

Here’s how I implemented it:

public void readImages(InputStream source,
                       Consumer<? super BufferedImage> imageHandler)
throws IOException {
    // Every image is at a byte index which is a multiple of this number.
    int boundary = 5000;

    try (ImageInputStream stream = ImageIO.createImageInputStream(source)) {
        while (true) {
            long pos = stream.getStreamPosition();

            Iterator<ImageReader> readers = ImageIO.getImageReaders(stream);
            if (!readers.hasNext()) {
                break;
            }

            ImageReader reader = readers.next();
            reader.setInput(stream);
            BufferedImage image = reader.read(0);

            imageHandler.accept(image);

            pos = stream.getStreamPosition();
            long bytesToSkip = boundary - (pos % boundary);
            if (bytesToSkip < boundary) {
                stream.skipBytes(bytesToSkip);
            }
        }
    }
}

And here’s how I tested it:

try (InputStream source = new BufferedInputStream(
        Files.newInputStream(Path.of(filename)))) {

    reader.readImages(source, img -> EventQueue.invokeLater(() -> {
        JOptionPane.showMessageDialog(null, new ImageIcon(img));
    }));
}
VGR
  • 40,506
  • 4
  • 48
  • 63