4

我正在更新一些旧代码以从 URL 而不是从数据库中获取一些二进制数据(数据即将从数据库中移出,而是可以通过 HTTP 访问)。数据库 API 似乎直接以原始字节数组的形式提供数据,并且有问题的代码使用 BufferedOutputStream 将该数组写入文件。

我对 Java 一点也不熟悉,但是通过谷歌搜索找到了这段代码:

URL u = new URL("my-url-string");
URLConnection uc = u.openConnection();
uc.connect();
InputStream in = uc.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
final int BUF_SIZE = 1 << 8;
byte[] buffer = new byte[BUF_SIZE];
int bytesRead = -1;
while((bytesRead = in.read(buffer)) > -1) {
    out.write(buffer, 0, bytesRead);
}
in.close();
fileBytes = out.toByteArray();

这似乎在大多数情况下都有效,但是当被复制的数据很大时我遇到了一个问题——我得到了一个 OutOfMemoryError 数据项,这些数据项在旧代码中运行良好。

我猜这是因为这个版本的代码同时在内存中有多个数据副本,而原始代码没有。

有没有一种简单的方法可以从 URL 中获取二进制数据并将其保存在文件中,而不会在内存中产生多个副本的成本?

4

4 回答 4

12

无需将数据写入字节数组,然后将其转储到文件中,您可以通过替换以下内容直接将其写入文件:

ByteArrayOutputStream out = new ByteArrayOutputStream();

和:

FileOutputStream out = new FileOutputStream("filename");

如果您这样做,则无需out.toByteArray()在最后调用。只需确保FileOutputStream在完成后关闭对象,如下所示:

out.close();

有关详细信息,请参阅FileOutputStream的文档。

于 2009-05-16T20:15:30.973 回答
1

我不知道“大”数据是什么意思,但请尝试使用 JVM 参数

java -Xmx 256m ...

它将最大堆大小设置为 256 MB(或您喜欢的任何值)。

于 2009-05-16T20:18:15.333 回答
1

如果您需要 Content-Length 并且您的 Web 服务器有点符合标准,那么它应该为您提供“Content-Length”标头。

URLConnection#getContentLength() 应该预先为您提供该信息,以便您能够创建文件。(请注意,如果您的 HTTP 服务器配置错误或受到邪恶实体的控制,则该标头可能与接收的字节数不匹配。在这种情况下,为什么不先流式传输到临时文件,然后再复制该文件?)

除此之外:ByteArrayInputStream 是一个可怕的内存分配器。它总是将缓冲区大小加倍,因此如果您读取一个 32MB + 1 字节的文件,那么您最终会得到一个 64MB 的缓冲区。实现一个自己的、更智能的字节数组流可能会更好,如下所示:

http://source.pentaho.org/pentaho-reporting/engines/classic/trunk/core/source/org/pentaho/reporting/engine/classic/core/util/MemoryByteArrayOutputStream.java

于 2009-05-17T10:25:01.107 回答
0

子类化 ByteArrayOutputStream 使您可以访问缓冲区和其中的字节数。

但是,当然,如果您只想将数据存储到文件中,则最好使用 FileOutputStream。

于 2009-05-16T20:16:28.537 回答