【问题标题】:Avoid obtaining same InputStream more than once避免多次获取相同的 InputStream
【发布时间】:2013-05-23 07:49:57
【问题描述】:

我可以看到有很多关于重用 InputStream 的帖子。我了解InputStream 是一次性的,不能重复使用。

但是,我有一个这样的用例:

我通过使用DropBox's Java SDK 获取DropBoxInputStreamDropBox 下载了文件。然后我需要通过传递InputStream 将文件上传到另一个系统。但是,作为下载的一部分,我必须提供文件的MD5。所以我必须在上传文件之前从流中读取文件。因为我收到的DropBoxInputStream只能使用一次,所以在我计算完MD5之后,上传文件之前,我必须再得到一个DropBoxInputStream。过程如下:

  1. 获取第一个 DropBoxInputStream
  2. 从 DropBoxInputStream 读取并计算 MD5
  3. 获取第二个 DropBoxInputStream
  4. 使用 MD5 和第二个 DropBoxInputStream 上传文件。

我在想,如果在计算MD5 之前我有很多方法可以“缓存”或“备份”InputStream,这样我就可以节省再次获得相同DropBoxInputStream 的第3 步?

非常感谢

编辑:

抱歉,我错过了一些信息。

我目前正在做的是使用MD5DigestOutputStream 来计算MD5。我通过MD5DigestOutputStream 流式传输数据并将它们保存为本地临时文件。一旦数据通过MD5DigestOutputStream,它将计算MD5

然后我调用第三方库使用计算出的 md5 和从临时文件读取的 FileInputStream 上传文件。

但是,有时这需要巨大的磁盘空间,我想消除使用临时文件的需要。我使用的库只接受MD5InputStream。这意味着我必须最终计算MD5。我的计划是使用我的MD5DigestOutputStream 将数据写入/dev/null(不保留文件),以便我可以计算MD5,并再次从DropBox 获取InputStream 并将其传递给我使用的库。我假设库将能够直接从DropBox 获取文件,而无需我将文件缓存在磁盘的内存中。它会起作用吗?

【问题讨论】:

  • 我已经得到了改进的计划。我确实得到了两次 InputStream,但性能还不错。通过这种方式,我不需要文件系统参与下载/上传过程。谢谢大家。

标签: java stream dropbox


【解决方案1】:

输入流并不是真正为创建副本或重用而设计的,它们专门用于您不想读取字节数组并对其使用数组操作的情况(当整个数组不可用,例如套接字通信)。您可以缓冲到一个字节数组中,这是从流中读取部分到一个字节数组缓冲区中的过程,直到您有足够的信息。

但这对于计算 md5 是不必要的。注意InputStream 是抽象的,所以它需要在扩展类中实现。它有许多实现——GZIPInputStream、fileinputstream 等。在设计模式中,这些是 IO 流的decorators:它们为抽象基 IO 类添加了额外的功能。例如,GZIPInputStream 向上压缩流。

所以,您需要一个流来为 md5 执行此操作。令人高兴的是,有一个有据可查的类似事情:请参阅this 答案。所以你应该能够传递你的 Dropbox 输入流(因为它本身就是一个输入流)来创建一个新的DigestInputStream,然后你就可以使用 md5 并像以前一样继续阅读。

担心类型转换? Java 中装饰器的想法是,由于InputStream 基类 接口了所有方法和执行 IO 所需的“牛肉”,因此传递从 @987654332 继承的对象实例没有害处@ 在每个流实现的构造函数中,你仍然可以做相同的核心 IO。

最后,我应该回答你的实际问题——说你仍然想要“缓存”或“备份”流?好吧,您可以将其写入字节数组。这是有据可查的,但是当您的流变得更加复杂时,这可能会变得很糟糕。或者,尝试查看PushbackInputStream。在这里,您可以轻松编写一个函数来读取 n 个字节,对它们执行和操作,然后将它们恢复到流中。在 Java 中避免这些流的实现通常很好,因为它不利于内存使用,但并不比缓冲所有你必须做的事情更糟糕。

或者,当然,我会选择DigestInputStream

希望对你有帮助,

最好的。

【讨论】:

  • 太棒了 - 所以你是对的 /dev/null;但也许我还没有理解,但你为什么不能一直使用输入流呢?你如何最终得到一个输出流?
  • 既然您将这些文件放在内存中,您不妨留在那里。尝试commons.apache.org/proper/commons-io/javadocs/api-1.4/org/… 填补任何空白。
【解决方案2】:

您无需从 DropBox 打开新的 InputStream。

从 DropBox 读取文件后,您就可以在本地获得它。所以它要么在内存中(在字节数组中),要么你将它存储在本地文件中。现在您可以创建一个 InputStream,从内存 (ByteArrayInputStream) 或磁盘 (FileInputStream) 读取数据以上传文件。

因此,您可以缓存内容(可以),而不是缓存 InputStream(您不能)。

【讨论】:

  • 感谢您的回答。这正是我目前正在做的事情。但我想避免使用本地文件进行缓存。
  • 如果您需要从 dropbox 读取流以计算 MD5,那么除了从您已经下载的文件中打开一个新的输入流之外别无选择。这比您想要做的更有效(重用 DropboxInputstream)。为什么?因为如果您能够重用 DropboxInputstream,那么您将从 Dropbox 下载文件两次,一次用于计算校验和,一次用于上传。
猜你喜欢
  • 2020-07-30
  • 2022-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多