自定义InputStream实现

8
为了将数据发送到我的FTP服务器上的文件,我需要创建一个自定义的InputStream实现,按行读取数据库数据,将其转换为CSV并通过其read()方法发布:从数据库中获取一个List对象。对于每个Application对象,我想在CSV文件中创建一行。
我的想法是在构造函数中加载所有数据,然后重写read方法。我需要重写InputStream的所有方法吗?我尝试过在Google上搜索一些示例,但没有成功 - 你能给我一个链接吗?

可能更容易的方法是将字节写入PipedOutputStream,然后从相应的PipedInputStream中读取:https://dev59.com/82025IYBdhLWcg3wzpaK#23874232 - AlexO
6个回答

13

您只需要实现没有参数的read()方法。所有其他方法都是对该方法的调用。出于性能和实现的便利性考虑,可能更容易实现具有三个参数的read()方法并使用那个方法重新实现无参的read()方法。


4
根据文档,您还需要实现 available() 方法。 - Mr Ed
1
Ed先生:它明确地说“应该”。而且既然你不应该依赖available()来知道要准备多少字节,我认为流使用默认实现也可以正常工作。 - Joachim Sauer
3
available() 的默认实现返回零。如果某个函数依赖这个值来确定是否可以读取数据,那么这个函数只有在 available() 函数正常工作时才能正常运行。 - Mr Ed
2
@Mr Ed:是的,那是真的。但这样的函数会出问题:available()只被定义为一个估计值。依赖于一个估计值的准确性是一个错误,在我看来。 - Joachim Sauer
3
最新文档中提到:“…InputStream类可用的方法始终返回0。这个方法应该被子类重写。返回值:在不阻塞的情况下从此输入流中可以读取(或跳过)的字节数的估计值,或者当到达输入流的末尾时返回0。”无论如何,我之所以提到这个方法,是因为我的程序没有定义这个方法而无法正常工作。 - Mr Ed
我的实际经验是,如果你只实现了read()方法,BufferedInputStream在尝试读取超过256个字符时会返回短读取。当我实现了available()方法后,它开始正常工作。 - stu

9

在实施我的InputStream时,我遇到了一些非常重要的问题。

  1. Override available(). As the Javadoc says:

    The available method for class InputStream always returns 0. This method should be overridden by subclasses.

    not overriding this method will causes that any tempt to test whether this stream is readable return false. For example, if you feed your inputStream to a inputStreamReader, this reader will always return false when you invoke reader.ready().

  2. return -1 in the read(). The doc didn't emphasize it:

    If no byte is available because the end of the stream has been reached, the value -1 is returned. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.

    if you choose to block read() when no data is available, you have to remember to return -1 at some situations. Not doing this may causes that another read(byte b[], int off, int len) blocks for the following code in the source:

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

    And this causes some(if not all) high level read block, like a reader's readLine(), read() etc.


4

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

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

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


那么我可以先将它用作输出流,提交所有数据,然后获取一个输入流,将所有数据提供给我的FTP客户端吗? - John Manak
是的和不是的,这应该比即时处理更容易。如果在读取时出现任何错误,则不会发送任何内容。如果您想要,在调试时可以显示整个内容。这取决于您。 - maaartinus

1

完全没有必要创建自定义的InputStream。只需使用ByteArrayInputStream,就像这样:

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

特别是考虑到这个引用:

我的想法是在构造函数中加载所有的数据,然后重写读取方法。

如果你这样做,通过实现一个定制的 InputStream ,你不会得到任何好处。这与我上面概述的方法几乎相同。


可能有数万个Application对象;每个对象在CSV文件中产生大约100个字符的一行。在内存中生成这样长的字符串是一个好主意还是最好创建一个临时文件,并在完成后传输它呢? - John Manak
@John 无论如何,我会创建一个通用接口,并将其传递给应用程序对象,然后我会尝试使用StringBuilderFile支持的版本来实现这个接口。 - Sean Patrick Floyd
我的理解是,如果数据大于2GB,它将会失败。如果我有误,请纠正我。 - Haseeb Jadoon

1
为什么需要自定义输入流?为什么不在生成CSV数据时将其写入正在写入FTP服务器的输出流中呢?

如果您可以将读取和写入分成单独的线程,那么这是一个不错的方法。 - Brent Bradburn
@nobar,这很少有用。唯一有用的情况是生产者和消费者都可以在其他资源上任意阻塞。 - jtahlborn

0

如果数据不太大,您可以:

  • 全部读取
  • 转换为CSV(文本)
  • 获取文本字节(通过String.getBytes(encoding)
  • 将字节数组放入ByteArrayInputStream

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接