Как я могу более эффективно загружать большие файлы через http? - PullRequest
0 голосов
/ 23 октября 2018

Я пытаюсь загрузить большие файлы (<1 ГБ) в Kotlin, так как я уже знал, что использую okhttp, и в основном следовал, просто воспользовавшись ответом <a href="https://stackoverflow.com/questions/25893030/download-binary-file-from-okhttp"> на этот вопрос .За исключением того, что я использую 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()

Это работает в том, что он загружает файл, не используя слишком много памяти, но кажется, что он излишне неэффективен, поскольку он постоянно пытаетсянаписать больше данных, не зная, поступили ли новые данные.Это также подтверждается моими собственными тестами, когда я выполняю это на ВМ с очень ограниченными ресурсами, так как кажется, что он использует больше ЦП, получая при этом более низкую скорость загрузки, чем сопоставимый скрипт на python, и, конечно, использует wget.

* 1008.* Что мне интересно, если есть способ, которым я могу дать что-то обратное, которое вызывается, если x байтов доступны, или если это конец файла, поэтому мне не нужно постоянно пытаться получить больше данных, не зная, есличто-то есть.

Редактировать: Если с okhttp это невозможно, у меня нет проблем с использованием чего-то другого, просто я привык к библиотеке http.

Ответы [ 2 ]

0 голосов
/ 23 октября 2018

Начиная с версии 11, Java имеет встроенный HttpClient , который реализует

асинхронные потоки данных с неблокирующим обратным давлением

и это то, что вам нужно, если вы хотите, чтобы ваш код выполнялся только при обработке данных.

Если вы можете позволить себе перейти на 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.")
}
0 голосов
/ 23 октября 2018

Можно покончить с BufferedInputStream.Или, поскольку его размер буфера по умолчанию в java Oracle равен 8192, используйте больший ByteArray, скажем, 4096.

Однако лучше всего использовать java.nio или попробовать Files.copy:

Files.copy(is, file.toPath());

Это удаляет около 12 строк кода.

Другой способ - отправить запрос с заголовком для дефлирования gzip сжатие Accept-Encoding: gzip, поэтому передача занимает меньше времени.В ответе здесь возможно добавьте is в new GZipInputStream(is) - когда дан заголовок ответа Content-Encoding: gzip.Или, если возможно, сохраните файл, сжатый с добавлением, заканчивающимся .gz;mybiography.md как mybiography.md.gz.

...