【问题标题】:Converting a Java byte reader to an InputStream将 Java 字节读取器转换为 InputStream
【发布时间】:2013-08-14 04:43:24
【问题描述】:

考虑一个通用的字节读取器,它实现了以下简单的 API,以从无法访问的数据结构中读取未指定数量的字节:

public interface ByteReader
{
    public byte[] read() throws IOException; // Returns null only at EOF
}

如何将上述内容有效地转换为标准Java InputStream,以便使用InputStream 类定义的所有方法的应用程序按预期工作?

一个简单的解决方案是将InputStream 子类化为

  1. 根据InputStreamread(...) 方法的需要调用ByteReaderread() 方法
  2. 缓冲在 byte[] 数组中检索到的字节
  3. 按预期返回字节数组的一部分,例如,每当调用 InputStream read() 方法时,一次返回 1 个字节。

但是,这需要更多的工作才能提高效率(例如,为了避免多字节数组分配)。此外,为了让应用程序扩展到较大的输入大小,将所有内容读入内存然后进行处理不是一种选择。

有什么可以使用的想法或开源实现吗?

【问题讨论】:

  • 检查了BufferedInputStream 源代码。我认为它使用了类似的想法,也许您可​​以将其扩展为从您的数据结构中读取
  • 是的,我在写问题之前已经检查过了。本质上需要一个类似fill()的函数,但是在ByteArrayInputStream实现中似乎太复杂了,所以我想知道是否有更简单的方法。
  • @PNS 您可以在ByteInputStream 的底层数组上使用Arrays.fill()
  • 这仅适用于将一个值设置为多个数组位置。 :-)
  • 不,你不能,但这不是问题的主要主题。在检查效率之前,必须先将 ByteReader 转换为 InputStream。

标签: java byte bytearray inputstream


【解决方案1】:

我假设,通过您使用“convert”,替换是可以接受的。

最简单的方法是使用ByteArrayInputStream,它已经提供了您正在寻找的所有功能(但必须包装现有数组),或者使用任何其他已经提供的InputStream从各种来源读取数据。

看来您可能正在冒着重新发明轮子的风险。如果可能的话,我会考虑完全废弃您的ByteReader 界面,而是使用以下选项之一:

  1. 替换为ByteInputStream
  2. 使用各种其他 InputStream 类(取决于数据源)。
  3. 使用您的自定义实现扩展 InputStream

我会坚持使用现有的 InputStream 类。我不知道您的代码是如何构建的,但您可以,例如,将getInputStream() 方法添加到您当前的数据源,并让它们返回一个适当的已经存在的InputStream(或必要时的自定义子类)。

顺便说一下,我建议在您自己的 IO 类中避免使用术语 Reader,因为在 Java SDK 中已经大量使用 Reader 来指示对编码字符数据进行操作的流读取器(而不是 InputStream它通常对原始字节数据进行操作)。

【讨论】:

  • 你的字节数据最初来自哪里? new BufferedInputStream(new ByteArrayInputStream(...))不够吗?
  • 在我的用例中没有原始数据访问,因此描述了 ByteReader 接口。
  • 了解数据的来源会很有帮助。是否可以将任何数据源替换为现有的InputStream 子类?
  • 没有必要或假设您会为我编写代码。覆盖 InputStream read(...) 方法是显而易见的解决方案,您的方法是正确的。我更喜欢重写 read(byte[]) 方法并重用缓冲区。再次感谢所有的帮助。 :-)
  • 查看我对您问题的评论。 ByteReaders 接口的一个结果是,无论您在顶部添加多少层缓冲,您永远无法更改消耗流所需的 read 调用总数。所以,老实说,就效率而言,你最好的选择是直接使用ByteReader,上面什么都没有;它确定自己的缓冲区大小这一事实阻止了所有进一步的缓冲优化。我很抱歉没有早点发现。
【解决方案2】:

围绕返回的数组创建多个ByteArrayInputStream 实例,并在提供连接的流中使用它们。例如,您可以为此使用 SequenceInputStream

诀窍是实现一个可以使用ByteReader 类的Enumeration<ByteArrayInputStream>

编辑:我已经实现了这个答案,但最好创建自己的 InputStream 实例。不幸的是,这个解决方案不能让你优雅地处理IOException


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);

【讨论】:

  • 我知道 SequenceInputStream 但使用 ByteArrayInputStream 既不高效也不同步。只有在没有足够的字节检索和缓冲以满足 InputStream read(...) 方法的需求时,才需要调用 ByteReader read() 方法。
  • new BufferedInputStream(new ByteArrayInputStream(...))不够用吗?
  • 不,请看我的 cmets 来回答。
  • 感谢和 +1 的实施,但效率不高。想象一下,必须在一个大文件上调用 ByteReader.read() 数千或数百万次......
  • 什么意思,效率不高?您为我们提供了一个界面,我使用了该界面。它不会创建任何额外的缓冲区。它唯一创建的是额外的对象,这些对象很小,很容易被垃圾收集。您向我们寻求帮助,但您认为您有足够的知识可以立即判断任何提交的内容。
猜你喜欢
  • 2010-11-18
  • 2010-09-23
  • 2014-06-13
相关资源
最近更新 更多