Использование Ktor HTTP Client (и kotlinx.io?) Для возобновляемых загрузок непосредственно в файл

Раньше я не работал с (большими) загрузками по протоколу HTTP, поэтому я не слишком разбираюсь в этой теме.

Итак, что я хочу сделать, так это программно загрузить файл из Интернета прямо в локальную файловую систему. Также было бы хорошо, если бы загрузка возобновлялась. Что касается Java, я нашел эту статью Baeldung, которая в основном объясняет, как именно это сделать.

Однако я работаю не с Java, а с Kotlin. В частности, я использую HTTP-клиент Ktor (с движком Apache, но при необходимости я могу его переключить). Хотелось бы узнать, есть ли возможность использовать этот клиент так же, как в упомянутой статье. По сути, я бы вызвал запрос GET, но я бы сказал клиенту передать байты в указанный Fileрасположение. Когда загрузка прерывается, я бы запросил размер файла для текущего статуса и возобновил его с этой позиции байта (PartialContent).

Я узнал что

  1. Ktor сервер поддерживает это: https://ktor.io/docs/partial-content.html
  2. В kotlinx-ioбиблиотека может оказаться полезной (из-за аналогии со статьей): https://github.com/Kotlin/kotlinx-io

Однако я не знаю, как собрать все вместе и "соединить незакрепленные провода", чтобы все заработало. Также кажется, что мой вопрос слишком конкретен для простого поиска в Google, поэтому сейчас я прибегаю к SO. Если бы кто-нибудь, имеющий некоторый опыт работы с Ktor и HTTP-загрузками, мог пролить свет на это для меня, я был бы очень признателен!

PS в дальнейшем может быть интересен ответ на этот ТАК вопрос.

Данные версии:

  • Котлин /JVM 1.4.20
  • Ктор 1.4.1
  • OpenJDK 11

1 ответ

Основная идея состоит в том, чтобы получить общую длину контента, создав HEADзапрашивать, а затем итеративно загружать контент с помощью заголовка Range.Вот пример реализации:

      suspend fun main() {
    val client = HttpClient()
    client.download(
        "https://weq714976.live-website.com/wp-content/uploads/2019/03/WeQ-Influencers-Logo-1568x812.png",
        File("result.png")
    )
}

suspend fun HttpClient.download(url: String, outFile: File, chunkSize: Int = 1024) {
    val length = head<HttpResponse>(url).headers[HttpHeaders.ContentLength]?.toLong() as Long
    val lastByte = length - 1

    var start = outFile.length()
    val output = FileOutputStream(outFile, true)

    while (true) {
        val end = min(start + chunkSize - 1, lastByte)

        get<HttpResponse>(url) {
            header("Range", "bytes=${start}-${end}")
        }.content.copyTo(output)

        if (end >= lastByte) break

        start += chunkSize
    }
}
Другие вопросы по тегам