問題描述
將 Java 字節讀取器轉換為 InputStream (Converting a Java byte reader to an InputStream)
Consider a generic byte reader implementing the following simple API to read an unspecified number of bytes from a data structure that is otherwise inaccessible:
public interface ByteReader
{
public byte[] read() throws IOException; // Returns null only at EOF
}
How could the above be efficiently converted to a standard Java InputStream, so that an application using all methods defined by the InputStream
class, works as expected?
A simple solution would be subclassing InputStream
to
- Call the
read()
method of theByteReader
as much as needed by theread(...)
methods of theInputStream
- Buffer the bytes retrieved in a byte[] array
- Return part of the byte array as expected, e.g., 1 byte at a time whenever the
InputStream read()
method is called.
However, this requires more work to be efficient (e.g., for avoiding multiple byte array allocations). Also, for the application to scale to large input sizes, reading everything into memory and then processing is not an option.
Any ideas or open source implementations that could be used?
參考解法
方法 1:
I assume, by your use of "convert", that a replacement is acceptable.
The easiest way to do this is to just use a ByteArrayInputStream
, which already provides all the features you are looking for (but must wrap an existing array), or to use any of the other already provided InputStream
for reading data from various sources.
It seems like you may be running the risk of reinventing wheels here. If possible, I would consider scrapping your ByteReader
interface entirely, and instead going with one of these options:
- Replace with
ByteInputStream
. - Use the various other
InputStream
classes (depending on the source of the data). - Extend
InputStream
with your custom implementation.
I'd stick to the existing InputStream
class everywhere. I have no idea how your code is structured but you could, for example, add a getInputStream()
method to your current data sources, and have them return an appropriate already‑existing InputStream
(or a custom subclass if necessary).
By the way, I recommend avoiding the term Reader
in your own IO classes, as Reader
is already heavily used in the Java SDK to indicate stream readers that operate on encoded character data (as opposed to InputStream
which generally operates on raw byte data).
方法 2:
Create multiple ByteArrayInputStream
instances around the returned arrays and use them in a stream that provides for concatenation. You could for instance use SequenceInputStream
for this.
Trick is to implement a Enumeration<ByteArrayInputStream>
that is can use the ByteReader
class.
EDIT: I've implemented this answer, but it is probably better to create your own InputStream
instance instead. Unfortunately, this solution does not let you handle IOException
gracefully.
final Enumeration<ByteArrayInputStream> basEnum = new Enumeration<ByteArrayInputStream>() {
ByteArrayInputStream baos;
boolean ended;
@Override
public boolean hasMoreElements() {
if (ended) {
return false;
}
if (baos == null) {
getNextBA();
if (ended) {
return false;
}
}
return true;
}
@Override
public ByteArrayInputStream nextElement() {
if (ended) {
throw new NoSuchElementException();
}
if (baos.available() != 0) {
return baos;
}
getNextBA();
return baos;
}
private void getNextBA() {
byte[] next;
try {
next = byteReader.read();
} catch (IOException e) {
throw new IllegalStateException("Issues reading byte arrays");
}
if (next == null) {
ended = true;
return;
}
this.baos = new ByteArrayInputStream(next);
}
};
SequenceInputStream sis = new SequenceInputStream(basEnum);
(by PNS、Jason C、Maarten Bodewes)