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 的最有效方法