У меня есть метод, который принимает InputStream и считывает данные из него. Я также хотел бы использовать этот метод с ByteBuffer. Есть ли способ обернуть ByteBuffer, чтобы он мог быть доступен как поток?
Обтекание ByteBuffer с помощью InputStream
Ответ 1
Ничего в JDK, но есть много реализаций, google для ByteBufferInputStream. В основном они обертывают один или несколько ByteBuffers и отслеживают индекс в них, который записывает, сколько уже было прочитано. Что-то вроде этого появляется много, но, по-видимому, глючит, см. @Mike Houston ответ для улучшения версия).
Ответ 2
Кажется, есть некоторые ошибки в реализации, упомянутые Thilo, а также скопированные и наклеенные на других сайтах дословно:
-
ByteBufferBackedInputStream.read()
возвращает знак расширенного внутреннего представления байта, который он читает, что неверно (значение должно находиться в диапазоне [-1.255]) -
ByteBufferBackedInputStream.read(byte[], int, int)
не возвращает -1, когда в буфере нет байтов, в соответствии со спецификацией API
ByteBufferBackedOutputStream выглядит относительно здоровым.
Я представляю "фиксированную" версию ниже. Если я нахожу больше ошибок (или кто-то их укажет), я обновлю его здесь.
Обновлено: удалено synchronized
ключевые слова из методов чтения/записи
InputStream
public class ByteBufferBackedInputStream extends InputStream {
ByteBuffer buf;
public ByteBufferBackedInputStream(ByteBuffer buf) {
this.buf = buf;
}
public int read() throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
return buf.get() & 0xFF;
}
public int read(byte[] bytes, int off, int len)
throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
len = Math.min(len, buf.remaining());
buf.get(bytes, off, len);
return len;
}
}
OutputStream
public class ByteBufferBackedOutputStream extends OutputStream {
ByteBuffer buf;
public ByteBufferBackedOutputStream(ByteBuffer buf) {
this.buf = buf;
}
public void write(int b) throws IOException {
buf.put((byte) b);
}
public void write(byte[] bytes, int off, int len)
throws IOException {
buf.put(bytes, off, len);
}
}
Ответ 3
Если он поддерживается массивом байтов, вы можете использовать ByteArrayInputStream
и получить массив байтов через ByteBuffer.array()
. Это вызовет исключение, если вы попробуете его на родном ByteBuffer.
Ответ 4
Это моя версия реализации InputStream
и OutputStream
:
ByteBufferBackedInputStream
:
public class ByteBufferBackedInputStream extends InputStream
{
private ByteBuffer backendBuffer;
public ByteBufferBackedInputStream(ByteBuffer backendBuffer) {
Objects.requireNonNull(backendBuffer, "Given backend buffer can not be null!");
this.backendBuffer = backendBuffer;
}
public void close() throws IOException {
this.backendBuffer = null;
}
private void ensureStreamAvailable() throws IOException {
if (this.backendBuffer == null) {
throw new IOException("read on a closed InputStream!");
}
}
@Override
public int read() throws IOException {
this.ensureStreamAvailable();
return this.backendBuffer.hasRemaining() ? this.backendBuffer.get() & 0xFF : -1;
}
@Override
public int read(@Nonnull byte[] buffer) throws IOException {
return this.read(buffer, 0, buffer.length);
}
@Override
public int read(@Nonnull byte[] buffer, int offset, int length) throws IOException {
this.ensureStreamAvailable();
Objects.requireNonNull(buffer, "Given buffer can not be null!");
if (offset >= 0 && length >= 0 && length <= buffer.length - offset) {
if (length == 0) {
return 0;
}
else {
int remainingSize = Math.min(this.backendBuffer.remaining(), length);
if (remainingSize == 0) {
return -1;
}
else {
this.backendBuffer.get(buffer, offset, remainingSize);
return remainingSize;
}
}
}
else {
throw new IndexOutOfBoundsException();
}
}
public long skip(long n) throws IOException {
this.ensureStreamAvailable();
if (n <= 0L) {
return 0L;
}
int length = (int) n;
int remainingSize = Math.min(this.backendBuffer.remaining(), length);
this.backendBuffer.position(this.backendBuffer.position() + remainingSize);
return (long) length;
}
public int available() throws IOException {
this.ensureStreamAvailable();
return this.backendBuffer.remaining();
}
public synchronized void mark(int var1) {
}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public boolean markSupported() {
return false;
}
}
ByteBufferBackedOutputStream
:
public class ByteBufferBackedOutputStream extends OutputStream
{
private ByteBuffer backendBuffer;
public ByteBufferBackedOutputStream(ByteBuffer backendBuffer) {
Objects.requireNonNull(backendBuffer, "Given backend buffer can not be null!");
this.backendBuffer = backendBuffer;
}
public void close() throws IOException {
this.backendBuffer = null;
}
private void ensureStreamAvailable() throws IOException {
if (this.backendBuffer == null) {
throw new IOException("write on a closed OutputStream");
}
}
@Override
public void write(int b) throws IOException {
this.ensureStreamAvailable();
backendBuffer.put((byte) b);
}
@Override
public void write(@Nonnull byte[] bytes) throws IOException {
this.write(bytes, 0, bytes.length);
}
@Override
public void write(@Nonnull byte[] bytes, int off, int len) throws IOException {
this.ensureStreamAvailable();
Objects.requireNonNull(bytes, "Given buffer can not be null!");
if ((off < 0) || (off > bytes.length) || (len < 0) ||
((off + len) > bytes.length) || ((off + len) < 0))
{
throw new IndexOutOfBoundsException();
}
else if (len == 0) {
return;
}
backendBuffer.put(bytes, off, len);
}
}
Ответ 5
Используйте буфер кучи (массив байтов) напрямую, если он доступен, в противном случае используйте wrapped bytebuffer (см. ответ Майк Хьюстон)
public static InputStream asInputStream(ByteBuffer buffer) {
if (buffer.hasArray()) {
// use heap buffer; no array is created; only the reference is used
return new ByteArrayInputStream(buffer.array());
}
return new ByteBufferInputStream(buffer);
}
Также обратите внимание, что обернутый буфер может эффективно поддерживать метку / reset и пропускать операции.