【问题标题】:How to put data from an OutputStream into a ByteBuffer?如何将 OutputStream 中的数据放入 ByteBuffer?
【发布时间】:2011-02-12 13:57:27
【问题描述】:

在 Java 中,我需要将 OutputStream 中的内容(我自己将数据填充到该流中)放入 ByteBuffer。如何用简单的方式来做?

【问题讨论】:

  • 一般情况下,您不能直接从输出流中读取。如果你自己填充数据,为什么不能也填充到ByteBuffer中呢?
  • 如果您创建继承表单 OutputStream 的类并使其可以直接从中读取,则可以间接读取它,也可以直接读取它。我自己没有填写数据,一些框架做了(以及我不想触及原因的框架代码)。另请参阅我的其他评论和答案。

标签: java outputstream bytebuffer


【解决方案1】:

您可以创建一个ByteArrayOutputStream 并对其进行写入,然后使用toByteArray() 将其内容提取为byte[]。然后ByteBuffer.wrap(byte []) 将使用输出字节数组的内容创建一个ByteBuffer

【讨论】:

  • 这正是我在阅读您的评论之前所做的。所以我可以批准这行得通:))
  • 不幸的是,ByteArrayOutputStream#toByteArray() 复制了底层字节数组,所以虽然这个方法很简单,但它并没有我们想要的那么高效。但是,由于ByteBuffer 的容量是固定的,因此通过OutputStream 将任意数量的数据写入ByteBuffer 的愿望是不可调和的。
  • @seh 不一定要这样 - 看看我的回答。
【解决方案2】:

@DJClayworth 的答案有一个更有效的变体。

正如@seh 正确注意到的那样,ByteArrayOutputStream.toByteArray() 返回支持byte[] 对象的副本,这可能效率低下。但是,支持byte[] 对象以及字节数都是ByteArrayOutputStream 类的受保护成员。因此,您可以创建自己的 ByteArrayOutputStream 变体,直接公开它们:

public class MyByteArrayOutputStream extends ByteArrayOutputStream {
  public MyByteArrayOutputStream() {
  }

  public MyByteArrayOutputStream(int size) {
    super(size);
  }

  public int getCount() {
    return count;
  }

  public byte[] getBuf() {
    return buf;
  }
}

使用这个类很简单:

MyByteArrayOutputStream out = new MyByteArrayOutputStream();
fillTheOutputStream(out);
return new ByteArrayInputStream(out.getBuf(), 0, out.getCount());

因此,一旦所有输出都被写入,同一个缓冲区就被用作输入流的基础。

【讨论】:

  • 好点,@mark。我的几个 Java 项目中有这样一个类,通常被限制为一个名为 PromiscuousByteArrayOutputStream 的包私有类。这个名字刻意长而且不祥。
  • getCount() 不需要,因为 ByteArrayOutputStream.size() 返回计数。
【解决方案3】:

尽管上述答案解决了您的问题,但它们都没有您对 NIO 的期望那样高效。 ByteArrayOutputStream 或 MyByteArrayOutputStream 首先将数据写入Java堆内存,然后将其复制到ByteBuffer,这对性能有很大影响。

一个有效的实现是自己编写 ByteBufferOutputStream 类。其实这很容易做到。您只需提供一个 write() 方法。请参阅此链接以获取 ByteBufferInputStream

【讨论】:

  • 由于我们可能需要为 OutputStream 重用现有的 ByteBuffer(及其底层数组),而不是分配新数组来获取新的 ByteBuffer,我相信这个答案更容易接受。
【解决方案4】:

尝试使用 PipedOutputStream 而不是 OutputStream。然后,您可以连接 PipedInputStream 以从 PipedOutputStream 中读取数据。

【讨论】:

  • 看起来不错。通过调用其方法 newInputStream 可以使用 ByteArrayOutputStream 完成类似的想法。但是,还有更直接的解决方案 - 将备份 ByteArrayOutputStream 的数组直接包装到 ByteBuffer 中。这就是我所做的。
【解决方案5】:

你是说你自己在写这个流?如果是这样,也许您可​​以实现自己的 ByteBufferOutputStream 并即插即用。

基类如下所示:

public class ByteBufferOutputStream extends OutputStream {
    //protected WritableByteChannel wbc; //if you need to write directly to a channel
    protected static int bs = 2 * 1024 * 1024; //2MB buffer size, change as needed
    protected ByteBuffer bb = ByteBuffer.allocate(bs);

    public ByteBufferOutputStream(...) {
        //wbc = ... //again for writing to a channel
    }

    @Override
    public void write(int i) throws IOException {
        if (!bb.hasRemaining()) flush();
        byte b = (byte) i;
        bb.put(b);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        if (bb.remaining() < len) flush();
        bb.put(b, off, len);
    }

    /* do something with the buffer when it's full (perhaps write to channel?)
    @Override
    public void flush() throws IOException {
        bb.flip();
        wbc.write(bb);
        bb.clear();
    }

    @Override
    public void close() throws IOException {
        flush();
        wbc.close();
    }
    /*
}

【讨论】:

    【解决方案6】:

    // 从扩展类访问受保护的成员buf & count

    class ByteArrayOutputStream2ByteBuffer extends ByteArrayOutputStream {
        public ByteBuffer toByteBuffer() {
            return ByteBuffer.wrap(buf, 0, count);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2010-10-09
      • 1970-01-01
      • 2014-03-30
      • 2021-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-25
      • 2019-09-13
      相关资源
      最近更新 更多