java - 将 InputStream 写入文件的内存友好方式
问题描述
我正在尝试为图像编写一个批量下载器。从 an 获取 InputStreamURLConnection
很容易,但下载所有文件需要一段时间。使用多线程确实可以加快速度,但是有很多线程下载文件可能会占用大量内存。这是我发现的:
让in
成为InputStream
、file
目标File
和fos
一个FileOutputStream
tofile
简单的方法
fos.write(in.readAllBytes());
读取整个文件,写入返回的byte[]
. 可能可用于获取网站源,对于较大的文件(例如图像)没有好处。
写块
byte[] buffer = new byte[bufsize];
int read;
while ((read = in.read(buffer, 0, bufsize)) >= 0) {
fos.write(buffer, 0, read);
}
对我来说似乎更好。
in.transferTo(fos)
in.transferTo(fos);
如上所示,在内部写入块。
文件.copy()
Files.copy(in, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
似乎使用本机实现。
当并行执行数十次时,我应该使用其中哪一个来最小化内存使用量?
这是一个有趣的小项目,外部库对于 IMO 来说太过分了。我也不能使用ImageIO
,因为它不能处理 webms、一些 pngs/jpgs 和动画 gif。
编辑:
这个问题是基于可以并发写入的假设。但是,情况似乎并非如此。我可能会同时获取图像链接,然后一个接一个地下载它们。无论如何,感谢您的回答!
解决方案
简短的回答是:从内存使用的角度来看,最好的解决方案是使用以块的形式读取和存储数据的版本。
缓冲区大小的选择基本上应考虑同时下载的数量、可用内存、下载速度和目标驱动器在数据传输率和 IOPS 方面的效率。
长答案是文件的并发下载并不一定意味着下载会更快。实际加快整体下载时间的同时下载数量主要取决于:
- 您从中下载的主机数量
- 您从中下载的主机的互联网连接速度,受此主机的网络适配器速度限制
- 您的互联网连接速度,受此主机的网络适配器速度限制
- 您从中下载的主机存储的 IOps
- 您正在下载到的存储的 IOps
- 您从中下载的主机上的存储传输速率
- 您正在下载到的存储的传输速率
- 本地和远程主机的性能。例如,一些较旧或低成本的安卓设备可能会受到 CPU 速度的限制。
例如,如果源主机有单个硬盘驱动器并且单个连接已经提供了完整的连接速度,那么使用多个连接是没有用的,因为它会通过创建切换 beetwen 传输文件的开销而使下载变慢。
也可能是源主机对单个连接有速度限制,因此多个连接可以加快速度。
HDD 驱动器的 IOPS 值通常在 80 IOPS 左右,传输速率在 80 MB/s 左右,这些因素可能会限制下载/上传速度。所以实际上你不能从这样的磁盘写入或读取超过每秒 80 个文件,并且超过大约 80MB/s 的传输限制,当然这几乎不取决于磁盘型号。
SSD 驱动器通常具有数万 IOPS 和 > 400 MB/s 的传输速率,因此限制要大得多,但对于真正快速的互联网连接,它们仍然很重要。
推荐阅读
- javascript - Coding Meetup #9 - 高阶函数系列 - 聚会年龄多样化吗?
- typescript - 将输入字段的值保存到变量中
- google-apps-script - Google Apps 脚本:如何在另一个工作表上处理来自一个工作表中的有界脚本的编辑事件?
- java - 如何在 android 中将 m3u 文件与 LibVlc 一起使用?
- pact - Pact JVM 无法加载类,因为它是用 Java15 编译的
- r - 如何将“char”列转换为大型数据集中的日期时间列
- python - 如何使打印出现在 tkinter 窗口中?
- cordova - 如何在我自己的 Cordova 插件中设置 SWIFT_VERSION
- swift - DateComponents 给出错误的时间
- node.js - Node.js:拒绝访问