将二进制数据从URL复制到Java中的文件,无需中间副本

时间:2009-05-16 20:08:38

标签: java file url copy

我正在更新一些旧代码,以便从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 个答案:

答案 0 :(得分:12)

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

ByteArrayOutputStream out = new ByteArrayOutputStream();

使用:

FileOutputStream out = new FileOutputStream("filename");

如果您这样做,则最后无需拨打电话out.toByteArray()。完成后请确保关闭FileOutputStream对象,如下所示:

out.close();

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

答案 1 :(得分:1)

我不知道你对“大”数据的意思,但尝试使用JVM参数

java -Xmx 256m ...

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

答案 2 :(得分:1)

如果您需要Content-Length并且您的Web服务器在某种程度上符合标准,那么它应该为您提供“内容长度”标题。

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

答案 3 :(得分:0)

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

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