InputStream 的自定义实现

Posted

技术标签:

【中文标题】InputStream 的自定义实现【英文标题】:Custom implementation of InputStream 【发布时间】:2011-06-15 20:48:02 【问题描述】:

要将数据发送到我的 FTP 服务器上的文件,我需要创建一个自定义 InputStream 实现,它逐行读取数据库数据,将其转换为 CSV 并通过其 read() 方法发布:从数据库中,我得到带有数据的 List<Application> 对象。对于每个Application 对象,我想在 CSV 文件中创建一行。

我的想法是在构造函数中加载所有数据,然后覆盖读取方法。我需要重写所有 InputStream 的方法吗?我尝试在谷歌上搜索一些示例,但没有成功 - 你最终能给我一个链接吗?

【问题讨论】:

将字节写入 PipedOutputStream 可能更容易,这些字节将从相应的 PipedOutputStream 中读取:***.com/a/23874232/1941359 【参考方案1】:

您只需要实现the read() method without parameters。所有其他方法都作为对该方法的调用来实现。出于性能原因(甚至是易于实现),实现the three-argument read() method instead 并根据该方法重新实现无参数read() 方法可能更容易。

【讨论】:

根据文档,您还需要实现 available() 方法。 先生。 Ed:它清楚地说“应该”。而且由于您不应该依赖available() 来知道要准备多少字节,所以我会说流在默认实现下可以正常工作。 available() 的默认实现返回零。如果某个函数依赖它来知道他们是否可以读取和获取某些东西,那么除非 available() 有效,否则该函数将无法工作。 @Mr Ed:是的,这是真的。但是这样的功能会被破坏:available() 仅被定义为估计值。在我看来,依赖估计是准确的是错误的。 最新的文档说:“...... InputStream 类的可用方法总是返回 0。这个方法应该被子类覆盖。返回:可以读取的字节数的估计(或跳过)从此输入流中无阻塞或到达输入流末尾时为 0。”无论如何,我首先提到它的唯一原因是因为我没有定义那个方法,所以我的程序没有工作。【参考方案2】:

我在实现 InputStream 时遇到的一些非常重要的点。

    覆盖可用()。正如 Javadoc 所说:

    InputStream 类的可用方法总是返回 0。 这个方法应该被子类覆盖。

    不覆盖此方法将导致任何尝试测试此流是否可读返回 false。例如,如果您将 inputStream 提供给 inputStreamReader,当您调用 reader.ready() 时,此阅读器将始终返回 false

    read() 中返回 -1。文档没有强调它:

    如果由于到达流的末尾而没有可用的字节,则返回值 -1。此方法会一直阻塞,直到输入数据可用、检测到流结束或引发异常。

    如果您选择在没有可用数据时阻止read(),则在某些情况下您必须记住return -1。不这样做可能会导致另一个read(byte b[], int off, int len) 阻止源中的以下代码:

    for (; i < len ; i++) // default len is a relative large number (8192 - readPosition)
        c = read();
        if (c == -1) 
            break;
        
        b[off + i] = (byte)c;
    
    

    这会导致一些(如果不是全部)高级读取块,例如读者的readLine(), read() 等。

【讨论】:

【参考方案3】:

对于可能较大的数据,您可以使用来自guava 的 com.google.common.io.FileBackedOutputStream

Javadoc:一个 OutputStream,它开始缓冲到一个字节数组,但一旦数据达到可配置的大小,就会切换到文件缓冲。

使用out.getSupplier().getInput(),您可以获得您的 InputStream。

【讨论】:

我可以先将其用作输出流,提交所有数据,然后获取输入流,将所有数据提供给我的 FTP 客户端吗? 是的,不是的,它应该比在飞行中做更容易。如果读取时出现任何错误,则不会发送任何内容。如果需要,您可以在调试时显示全部内容。这取决于你。【参考方案4】:

完全没有必要创建自定义InputStream。使用ByteArrayInputStream,类似这样:

public static InputStream createStream()
    final String csv = createCsvFromDataBaseValues();
    return new ByteArrayInputStream(csv.getBytes());

特别是给出这句话:

我的想法是加载所有数据 构造函数,然后覆盖读取 方法。

如果您这样做,则通过实现自定义InputStream 将一无所获。它几乎等同于我上面概述的方法。

【讨论】:

它可能在数以万计的Application对象中;每个在 CSV 文件中生成大约 100 个字符的行。在内存中生成这么长的字符串是个好主意,还是制作一个临时文件并在完成后传输它会更好? @John 无论哪种方式,我都会创建一个传递给 Application 对象的通用接口,并且我会尝试使用此接口的 StringBuilder- 和 File 支持的版本.【参考方案5】:

为什么需要自定义输入流?为什么不直接将生成的 csv 数据写入正在写入 ftp 服务器的输出流?

【讨论】:

如果您可以将读写分区到separate threads,这是一个不错的方法。 @nobar,这很少有用。唯一有用的事情是生产者和消费者都可以任意阻止其他一些资源。【参考方案6】:

如果数据不是太大,你可以:

阅读全文 转换为 CSV(文本) 获取文本字节(通过String.getBytes(encoding)) 但是ByteArrayInputStream中的字节数组

【讨论】:

以上是关于InputStream 的自定义实现的主要内容,如果未能解决你的问题,请参考以下文章

从 OutputStream 创建 InputStream 的最有效方法

从 OutputStream 创建 InputStream 的最有效方法

如何读取服务器套接字 JAVA 中的所有 Inputstream

使用多线程实现多个客户端与服务端通信1

四大IO抽象类

Http