如何更有效地通过 http 下载大文件?
How can I more efficently download large files over http?
我正在尝试在 Kotlin 中下载大文件 (<1GB),因为我已经知道我正在使用 okhttp 并且几乎只是使用了来自 this question 的答案。除了我使用的是 Kotlin 而不是 java,所以语法略有不同。
val client = OkHttpClient()
val request = Request.Builder().url(urlString).build()
val response = client.newCall(request).execute()
val is = response.body().byteStream()
val input = BufferedInputStream(is)
val output = FileOutputStream(file)
val data = ByteArray(1024)
val total = 0L
val count : Int
do {
count = input.read(data)
total += count
output.write(data, 0, count)
} while (count != -1)
output.flush()
output.close()
input.close()
它的工作原理是它在不使用太多内存的情况下下载文件,但它似乎不必要地无效,因为它不断尝试写入更多数据而不知道是否有新数据到达。
我自己的测试似乎也证实了这一点,而 运行 这是在资源非常有限的 VM 上,因为它似乎使用更多 CPU 而下载速度低于 python 中的可比较脚本,并且使用 wget
.
的原因
我想知道是否有一种方法可以让我在 x 字节可用或文件末尾时调用回调,这样我就不必不断尝试获取更多不知道有没有数据
编辑:
如果用 okhttp 不可能,我使用其他东西没有问题,只是它是我习惯的 http 库。
可以取消 BufferedInputStream。或者因为它在 Oracle java 中的默认缓冲区大小是 8192,所以使用更大的 ByteArray,比如 4096。
不过最好是使用 java.nio 或尝试 Files.copy:
Files.copy(is, file.toPath());
这删除了大约 12 行代码。
另一种方法是发送带有header 的请求以放气gzip 压缩Accept-Encoding: gzip
,因此传输时间更短。在此处的响应中,然后可能将 is
包装在 new GZipInputStream(is)
中 - 当给出响应 header Content-Encoding: gzip
时。或者,如果可行,存储压缩后的文件,并以 .gz
结尾; mybiography.md
作为 mybiography.md.gz
.
从版本 11 开始,Java 有一个内置的 HttpClient 实现了
asynchronous streams of data with non-blocking back pressure
如果您希望代码仅在有数据要处理时 运行,这就是您所需要的。
如果您有能力升级到 Java 11,您将能够使用 HttpResponse.BodyHandlers.ofFile 正文处理程序开箱即用地解决您的问题。您不必自己实现任何数据传输逻辑。
Kotlin 示例:
fun main(args: Array<String>) {
val client = HttpClient.newHttpClient()
val request = HttpRequest.newBuilder()
.uri(URI.create("https://www.google.com"))
.GET()
.build()
println("Starting download...")
client.send(request, HttpResponse.BodyHandlers.ofFile(Paths.get("google.html")))
println("Done with download.")
}
我正在尝试在 Kotlin 中下载大文件 (<1GB),因为我已经知道我正在使用 okhttp 并且几乎只是使用了来自 this question 的答案。除了我使用的是 Kotlin 而不是 java,所以语法略有不同。
val client = OkHttpClient()
val request = Request.Builder().url(urlString).build()
val response = client.newCall(request).execute()
val is = response.body().byteStream()
val input = BufferedInputStream(is)
val output = FileOutputStream(file)
val data = ByteArray(1024)
val total = 0L
val count : Int
do {
count = input.read(data)
total += count
output.write(data, 0, count)
} while (count != -1)
output.flush()
output.close()
input.close()
它的工作原理是它在不使用太多内存的情况下下载文件,但它似乎不必要地无效,因为它不断尝试写入更多数据而不知道是否有新数据到达。
我自己的测试似乎也证实了这一点,而 运行 这是在资源非常有限的 VM 上,因为它似乎使用更多 CPU 而下载速度低于 python 中的可比较脚本,并且使用 wget
.
我想知道是否有一种方法可以让我在 x 字节可用或文件末尾时调用回调,这样我就不必不断尝试获取更多不知道有没有数据
编辑: 如果用 okhttp 不可能,我使用其他东西没有问题,只是它是我习惯的 http 库。
可以取消 BufferedInputStream。或者因为它在 Oracle java 中的默认缓冲区大小是 8192,所以使用更大的 ByteArray,比如 4096。
不过最好是使用 java.nio 或尝试 Files.copy:
Files.copy(is, file.toPath());
这删除了大约 12 行代码。
另一种方法是发送带有header 的请求以放气gzip 压缩Accept-Encoding: gzip
,因此传输时间更短。在此处的响应中,然后可能将 is
包装在 new GZipInputStream(is)
中 - 当给出响应 header Content-Encoding: gzip
时。或者,如果可行,存储压缩后的文件,并以 .gz
结尾; mybiography.md
作为 mybiography.md.gz
.
从版本 11 开始,Java 有一个内置的 HttpClient 实现了
asynchronous streams of data with non-blocking back pressure
如果您希望代码仅在有数据要处理时 运行,这就是您所需要的。
如果您有能力升级到 Java 11,您将能够使用 HttpResponse.BodyHandlers.ofFile 正文处理程序开箱即用地解决您的问题。您不必自己实现任何数据传输逻辑。
Kotlin 示例:
fun main(args: Array<String>) {
val client = HttpClient.newHttpClient()
val request = HttpRequest.newBuilder()
.uri(URI.create("https://www.google.com"))
.GET()
.build()
println("Starting download...")
client.send(request, HttpResponse.BodyHandlers.ofFile(Paths.get("google.html")))
println("Done with download.")
}