使用 ktor 将文件上传到电报 bot api

Posted

技术标签:

【中文标题】使用 ktor 将文件上传到电报 bot api【英文标题】:Uploading files to telegram bot api using ktor 【发布时间】:2019-05-07 11:25:06 【问题描述】:

我正在使用 kotlin 和 ktor 为电报机器人 API 编写包装器。 我有一个问题 - 找不到上传文件的工作方式。

(来自 tg bot api docs)发送文件有三种方式(照片、贴纸、音频、媒体等):

    如果文件已经存储在 Telegram 服务器的某个位置,则无需重新上传:每个文件对象都有一个 file_id 字段,只需将此 file_id 作为参数传递,而不是上传。以这种方式发送的文件没有限制。 为 Telegram 提供要发送的文件的 HTTP URL。 Telegram 将下载并发送文件。照片最大为 5 MB,其他类型的内容最大为 20 MB。 使用 multipart/form-data 以通常的方式发布文件,即通过浏览器上传文件。照片最大 10 MB,其他文件最大 50 MB。

第一种和第二种方法我没有任何问题。

现在我有一个丑陋的函数,它向 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
        )

我已经在不同的文件上对其进行了测试,我发现:

    如果我在 17,9 KiB56,6 KiB 之间发送文件,我会从 tg 得到以下错误:Bad Request: wrong URL host

    如果我在75,6 KiB913,2 KiB 之间发送文件,我会收到错误413 Request Entity Too Large

* 我使用的是sendDocument 方法

使用ktor发送文件的真正方法是什么?

【问题讨论】:

我是否正确理解您的实际问题是如何使用普通 Kotlin 或 Ktor 上传 multipart/form-data,而 Telegram bot 并不真正相关? @AlexeySoshin 是的 【参考方案1】:

好吧,我终于找到答案了。修复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)
    

【讨论】:

以上是关于使用 ktor 将文件上传到电报 bot api的主要内容,如果未能解决你的问题,请参考以下文章

使用 Team 的附件按钮将文件上传到 bot 应用程序时,Microsoft Teams 返回错误

Telegram 通过 bot api 将成员添加到频道

使用电报机器人 api、python 3 和 JobQueue 将消息发送到通道的方法

使用 pyTelegramBotAPI 和 Telegram Bot API 的区别

如何将电报机器人添加到我不是管理员的公共频道?

getUpdates Telegram bot API 中忽略的偏移量参数