Загрузка файлов в telegram bot api с помощью ktor - PullRequest
0 голосов
/ 05 декабря 2018

Я пишу оболочку для telegram-bot-api, используя kotlin и ktor .У меня проблема - не могу найти рабочий способ загрузки файлов.

(из tg bot api docs )
Существует три способа отправки файлов (фотографии,наклейки, аудио, мультимедиа и т. д.):

  1. Если файл уже хранится где-то на серверах Telegram, его не нужно повторно загружать: каждый объект файла имеет поле file_idпросто передайте этот file_id в качестве параметра вместо загрузки.Для файлов, отправляемых таким способом, ограничений нет.
  2. Предоставьте Telegram URL-адрес HTTP для отправляемого файла.Telegram загрузит и отправит файл.Максимальный размер 5 МБ для фотографий и 20 МБ для других типов содержимого.
  3. Разместите файл, используя multipart / form-data, обычным образом, чтобы файлы загружались через браузер.Максимальный размер 10 МБ для фотографий, 50 МБ для других файлов.

С 1-м и 2-м способами у меня нет проблем.

Пока у меня есть эта уродливая функция, которая делает запрос на tg и анализирует ответ:

internal suspend inline fun <reified T> makeRequest(token: String, method: TelegramMethod, vararg params: Pair<String, Any?>, files: Map<String, String> = emptyMap()): T {
    try {
        val data: List<PartData> = formData {
            files.forEach { key, fileName ->
                append(key, Files.newInputStream(Paths.get(fileName)).asInput())
            }
        }
        val response = client.submitFormWithBinaryData<HttpResponse>(data) {
            this.method = HttpMethod.Post
            url {
                protocol = URLProtocol("https", 42)
                host = API_HOST
                encodedPath = API_PATH_PATTERN.format(token, method.methodName)
                params.forEach { (name, value) ->
                    if (value != null) { this.parameters[name] = value as String }
                }
            }
        }
        val result = response.receive<String>()
        return parseTelegramAnswer<T>(response, result)
    } catch (e: BadResponseStatusException) {
        val answer = mapper.readValue<TResult<T>>(e.response.content.readUTF8Line()!!)
        throw checkTelegramError(e.response.status, answer)
    }
}

Без файлов это работает, с файлами - нет.(Я думаю, что я все делаю неправильно)

Пример использования:

suspend fun getUpdates(offset: Long? = null, limit: Int? = null, timeout: Int? = null, allowedUpdates: List<String>? = null): List<Update> =
        api.makeRequest(
            token,
            TelegramMethod.getUpdates,
            "offset" to offset?.toString(),
            "limit" to limit?.toString(),
            "timeout" to timeout?.toString(),
            "allowed_updates" to allowedUpdates
        )

Я протестировал его на разных файлах и обнаружил, что:

  1. если я отправляю файлы между 17,9 KiB и 56,6 KiB, я получаю от tg следующую ошибку: Bad Request: wrong URL host

  2. если я отправляю файлы между 75,6 KiB и 913,2 KiB, я получаю ошибку413 Request Entity Too Large

* Я использовал sendDocument метод

Каков истинный способ отправки файлов с использованием ktor?

1 Ответ

0 голосов
/ 11 декабря 2018

Хорошо, я наконец-то нашел ответ.Исправлена ​​makeRequest функция:

internal suspend inline fun <reified T> makeRequest(token: String, method: TelegramMethod, vararg params: Pair<String, Any?>): T {
    try {
        val response = client.submitForm<HttpResponse> {
            this.method = HttpMethod.Post
            url {
                protocol = URLProtocol.HTTPS
                host = API_HOST
                encodedPath = API_PATH_PATTERN.format(token, method.methodName)
            }
            body = MultiPartFormDataContent(
                    formData {
                        params.forEach { (key, value) ->
                            when (value) {
                                null -> {}
                                is MultipartFile -> append(
                                        key,
                                        value.file.inputStream().asInput(),
                                        Headers.build {
                                            append(HttpHeaders.ContentType, value.mimeType)
                                            append(HttpHeaders.ContentDisposition, "filename=${value.filename}")
                                        }
                                )
                                is FileId -> append(key, value.fileId)
                                else -> append(key, value.toString())
                            }
                        }
                    }
            )
        }
        val result = response.receive<String>()
        val r = parseTelegramAnswer<T>(response, result)
        return r
    } catch (e: BadResponseStatusException) {
        val answer = mapper.readValue<TResult<T>>(e.response.content.readUTF8Line()!!)
        throw checkTelegramError(e.response.status, answer)
    }
}
...