diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt index 87f6503f729..3f91d354e04 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt @@ -80,15 +80,18 @@ inline fun FormBuilder.append(key: String, headers: Headers = Headers.Empty, bod } /** - * Append a form part with the specified [key] and [filename] using [bodyBuilder] for it's body + * Append a form part with the specified [key], [filename] and optional [contentType] using [bodyBuilder] for it's body */ @UseExperimental(ExperimentalContracts::class) -fun FormBuilder.append(key: String, filename: String, bodyBuilder: BytePacketBuilder.() -> Unit) { +fun FormBuilder.append(key: String, filename: String, contentType: ContentType? = null, bodyBuilder: BytePacketBuilder.() -> Unit) { contract { callsInPlace(bodyBuilder, InvocationKind.EXACTLY_ONCE) } - val filenameHeader: Headers = headersOf( - HttpHeaders.ContentDisposition, "filename=$filename" - ) - append(key, filenameHeader, bodyBuilder) + + val headersBuilder = HeadersBuilder() + headersBuilder[HttpHeaders.ContentDisposition] ="filename=$filename" + contentType?.run { headersBuilder[HttpHeaders.ContentType] = this.toString() } + val headers = headersBuilder.build() + + append(key, headers, bodyBuilder) } diff --git a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/ContentTest.kt b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/ContentTest.kt index 8cb42aa410c..1d7f8f8e1f1 100644 --- a/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/ContentTest.kt +++ b/ktor-client/ktor-client-tests/jvm/src/io/ktor/client/tests/ContentTest.kt @@ -172,6 +172,11 @@ abstract class ContentTest(private val factory: HttpClientEngineFactory<*>) : Te writeByte(i.toByte()) } } + append("file2", "urlencoded_name2.jpg", ContentType.Application.OctetStream) { + for (i in 1..4096) { + writeByte(i.toByte()) + } + } append("hello", 5) } } @@ -191,12 +196,13 @@ abstract class ContentTest(private val factory: HttpClientEngineFactory<*>) : Te } } - private fun filenameAndContentString(provider: () -> Input, headers: Headers): String { + private fun filenameContentTypeAndContentString(provider: () -> Input, headers: Headers): String { val dispHeader: String = headers.getAll(HttpHeaders.ContentDisposition)!!.joinToString(";") val disposition: ContentDisposition = ContentDisposition.parse(dispHeader) val filename: String = disposition.parameter("filename") ?: "" + val contentType = headers[HttpHeaders.ContentType]?.let { ContentType.parse(it) } ?: "" val content: String = provider().readText(Charsets.ISO_8859_1) - return "$filename$content" + return "$filename$contentType$content" } private fun List.makeString(): String = buildString { @@ -204,9 +210,9 @@ abstract class ContentTest(private val factory: HttpClientEngineFactory<*>) : Te list.forEach { appendln(it.name!!) val content = when (it) { - is PartData.FileItem -> filenameAndContentString(it.provider, it.headers) + is PartData.FileItem -> filenameContentTypeAndContentString(it.provider, it.headers) is PartData.FormItem -> it.value - is PartData.BinaryItem -> filenameAndContentString(it.provider, it.headers) + is PartData.BinaryItem -> filenameContentTypeAndContentString(it.provider, it.headers) } appendln(content)