我正在更新一些旧代码,以便从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中获取二进制数据并将其保存在文件中,而不会在内存中产生多个副本的成本?
答案 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缓冲区。实现一个更聪明的字节数组流可能更好,就像这样:
答案 3 :(得分:0)
子类化ByteArrayOutputStream使您可以访问缓冲区及其中的字节数。
但是,当然,如果您只想将de数据存储到文件中,最好使用FileOutputStream。