kotlintelegramktor

ktor client post multipart/form-data


How can I post file as multipart/form-data use ktor client? I want to use it for telegram bot API "send document". I need to achieve the same result as the curl command

curl -F document=@"path/to/some.file" https://api.telegram.org/bot<token>/sendDocument?chat_id=<chat_id>

Solution

  • You can use the submitFormWithBinaryData method to send mutlipart/form-data request. Here is a solution:

    val client = HttpClient(Apache) {}
    
    val file = File("path/to/some.file")
    val chatId = "123"
    
    client.submitFormWithBinaryData(
        url = "https://api.telegram.org/bot<token>/sendDocument?chat_id=$chatId",
        formData = formData {
            append("document", file.readBytes(), Headers.build {
                append(HttpHeaders.ContentDisposition, "filename=${file.name}")
            })
        }
    )
    

    Here is a code (generated with my tool c2k), where the entire contents of the file isn't read into memory:

    import io.ktor.client.HttpClient
    import io.ktor.client.request.forms.ChannelProvider
    import io.ktor.client.request.forms.MultiPartFormDataContent
    import io.ktor.client.request.forms.formData
    import io.ktor.client.request.post
    import io.ktor.client.request.setBody
    import io.ktor.client.statement.bodyAsText
    import io.ktor.http.Headers
    import io.ktor.http.HttpHeaders
    import io.ktor.util.cio.readChannel
    import java.io.File
    import kotlinx.coroutines.runBlocking
    
    fun main() = runBlocking {
        val client = HttpClient {
            followRedirects = false
        }
        val response = client.post("https://api.telegram.org/bot<token>/sendDocument?chat_id=<chat_id>") {
            setBody(MultiPartFormDataContent(formData {
                val some = File("path/to/some.file")
                append("document", ChannelProvider(size = some.length()) { some.readChannel() }, Headers.build {
                    append(HttpHeaders.ContentType, "application/octet-stream")
                    append(HttpHeaders.ContentDisposition, "filename=\"${some.name}\"")
                })
            }))
        }
        print(response.bodyAsText())
    }