0
# Form Data Handling
1
2
URL-encoded and multipart form data support for form submissions and file uploads with type-safe DSL builders.
3
4
## Capabilities
5
6
### FormDataContent Class
7
8
URL-encoded form data content for application/x-www-form-urlencoded submissions.
9
10
```kotlin { .api }
11
/**
12
* Content for URL-encoded form data submissions
13
*/
14
class FormDataContent(
15
val formData: Parameters
16
) : OutgoingContent.ByteArrayContent() {
17
/** Content length in bytes */
18
override val contentLength: Long
19
20
/** Content type for URL-encoded form data */
21
override val contentType: ContentType = ContentType.Application.FormUrlEncoded
22
23
/**
24
* Get form data as byte array
25
* @return URL-encoded form data bytes
26
*/
27
override fun bytes(): ByteArray
28
}
29
```
30
31
**Usage Examples:**
32
33
```kotlin
34
import io.ktor.client.*
35
import io.ktor.client.request.*
36
import io.ktor.client.request.forms.*
37
import io.ktor.http.*
38
39
val client = HttpClient()
40
41
// Create form data content
42
val formData = Parameters.build {
43
append("username", "john.doe")
44
append("password", "secretpassword")
45
append("remember", "true")
46
}
47
48
val response = client.post("https://api.example.com/login") {
49
setBody(FormDataContent(formData))
50
}
51
```
52
53
### MultiPartFormDataContent Class
54
55
Multipart form data content for file uploads and complex form submissions.
56
57
```kotlin { .api }
58
/**
59
* Content for multipart form data submissions
60
*/
61
class MultiPartFormDataContent(
62
parts: List<PartData>,
63
val boundary: String = generateBoundary(),
64
override val contentType: ContentType = ContentType.MultiPart.FormData
65
.withParameter("boundary", boundary)
66
) : OutgoingContent.WriteChannelContent() {
67
/** Content length, null for streaming */
68
override var contentLength: Long? = null
69
70
/**
71
* Write multipart content to channel
72
* @param channel Output channel for writing data
73
*/
74
override suspend fun writeTo(channel: ByteWriteChannel)
75
}
76
```
77
78
### Form Submission Functions
79
80
Convenient functions for submitting forms without manually creating content objects.
81
82
```kotlin { .api }
83
/**
84
* Submit URL-encoded form data
85
* @param url Target URL
86
* @param formParameters Form parameters to submit
87
* @param encodeInQuery Whether to encode parameters in URL query string
88
* @param block Additional request configuration
89
* @return HttpResponse from server
90
*/
91
suspend fun HttpClient.submitForm(
92
url: String,
93
formParameters: Parameters = Parameters.Empty,
94
encodeInQuery: Boolean = false,
95
block: HttpRequestBuilder.() -> Unit = {}
96
): HttpResponse
97
98
/**
99
* Submit multipart form data with binary data
100
* @param url Target URL
101
* @param formData List of form parts including files
102
* @param block Additional request configuration
103
* @return HttpResponse from server
104
*/
105
suspend fun HttpClient.submitFormWithBinaryData(
106
url: String,
107
formData: List<PartData>,
108
block: HttpRequestBuilder.() -> Unit = {}
109
): HttpResponse
110
```
111
112
**Usage Examples:**
113
114
```kotlin
115
import io.ktor.client.*
116
import io.ktor.client.request.forms.*
117
import io.ktor.http.*
118
119
val client = HttpClient()
120
121
// Submit simple form
122
val response = client.submitForm(
123
url = "https://api.example.com/contact",
124
formParameters = Parameters.build {
125
append("name", "John Doe")
126
append("email", "john@example.com")
127
append("message", "Hello from Ktor!")
128
}
129
)
130
131
// Submit form with file upload
132
val formData = formData {
133
append("name", "John Doe")
134
append("file", fileBytes, Headers.build {
135
append(HttpHeaders.ContentDisposition, "filename=\"document.pdf\"")
136
append(HttpHeaders.ContentType, "application/pdf")
137
})
138
}
139
140
val uploadResponse = client.submitFormWithBinaryData(
141
url = "https://api.example.com/upload",
142
formData = formData
143
)
144
```
145
146
### Form Building DSL
147
148
Type-safe DSL for building multipart form data with support for text fields, files, and binary data.
149
150
```kotlin { .api }
151
/**
152
* Build form data using DSL
153
* @param block Form building block
154
* @return List of form parts
155
*/
156
fun formData(block: FormBuilder.() -> Unit): List<PartData>
157
158
/**
159
* Builder for constructing multipart form data
160
*/
161
class FormBuilder {
162
/**
163
* Append text field to form
164
* @param key Field name
165
* @param value Field value
166
*/
167
fun append(key: String, value: String)
168
169
/**
170
* Append text field with custom headers
171
* @param key Field name
172
* @param value Field value
173
* @param headers Custom headers for the part
174
*/
175
fun append(key: String, value: String, headers: Headers)
176
177
/**
178
* Append binary data to form
179
* @param key Field name
180
* @param value Binary data as byte array
181
* @param headers Custom headers for the part
182
*/
183
fun append(key: String, value: ByteArray, headers: Headers = Headers.Empty)
184
185
/**
186
* Append data from Input provider
187
* @param key Field name
188
* @param provider Function that provides Input stream
189
* @param headers Custom headers for the part
190
*/
191
fun append(
192
key: String,
193
provider: () -> Input,
194
headers: Headers = Headers.Empty
195
)
196
197
/**
198
* Append data from ByteReadChannel provider
199
* @param key Field name
200
* @param provider Function that provides ByteReadChannel
201
* @param headers Custom headers for the part
202
*/
203
fun append(
204
key: String,
205
provider: () -> ByteReadChannel,
206
headers: Headers = Headers.Empty
207
)
208
}
209
```
210
211
**Usage Examples:**
212
213
```kotlin
214
import io.ktor.client.*
215
import io.ktor.client.request.forms.*
216
import io.ktor.http.*
217
import io.ktor.utils.io.*
218
import java.io.File
219
220
val client = HttpClient()
221
222
// Build complex form with various field types
223
val formData = formData {
224
// Text fields
225
append("username", "john.doe")
226
append("description", "User profile update")
227
228
// File upload from byte array
229
append("avatar", avatarBytes, Headers.build {
230
append(HttpHeaders.ContentDisposition, "filename=\"avatar.jpg\"")
231
append(HttpHeaders.ContentType, "image/jpeg")
232
})
233
234
// File upload from File (JVM)
235
append("document", File("document.pdf").readBytes(), Headers.build {
236
append(HttpHeaders.ContentDisposition, "filename=\"document.pdf\"")
237
append(HttpHeaders.ContentType, "application/pdf")
238
})
239
240
// Streaming data
241
append("data", { ByteReadChannel(largeDataBytes) }, Headers.build {
242
append(HttpHeaders.ContentDisposition, "filename=\"data.bin\"")
243
append(HttpHeaders.ContentType, "application/octet-stream")
244
})
245
}
246
247
val response = client.submitFormWithBinaryData(
248
url = "https://api.example.com/profile/update",
249
formData = formData
250
) {
251
header("Authorization", "Bearer $token")
252
}
253
```
254
255
### PartData Types
256
257
Data classes representing different types of form parts.
258
259
```kotlin { .api }
260
/**
261
* Base class for form parts
262
*/
263
sealed class PartData {
264
/** Headers for this part */
265
abstract val headers: Headers
266
267
/** Release resources associated with this part */
268
abstract fun dispose()
269
}
270
271
/**
272
* Form part containing text data
273
*/
274
class PartData.FormItem(
275
val value: String,
276
override val headers: Headers
277
) : PartData()
278
279
/**
280
* Form part containing binary data
281
*/
282
class PartData.BinaryItem(
283
val provider: () -> Input,
284
override val headers: Headers
285
) : PartData()
286
287
/**
288
* Form part containing binary channel data
289
*/
290
class PartData.BinaryChannelItem(
291
val provider: () -> ByteReadChannel,
292
override val headers: Headers
293
) : PartData()
294
295
/**
296
* Form part containing file data
297
*/
298
class PartData.FileItem(
299
val provider: () -> Input,
300
override val headers: Headers
301
) : PartData()
302
```
303
304
### Content-Disposition Headers
305
306
Utilities for working with Content-Disposition headers in form data.
307
308
```kotlin { .api }
309
/**
310
* Content-Disposition header utilities
311
*/
312
object ContentDisposition {
313
/**
314
* Create Content-Disposition header for form field
315
* @param name Field name
316
* @return Content-Disposition header value
317
*/
318
fun formData(name: String): String = "form-data; name=\"$name\""
319
320
/**
321
* Create Content-Disposition header for file field
322
* @param name Field name
323
* @param filename File name
324
* @return Content-Disposition header value
325
*/
326
fun formData(name: String, filename: String): String =
327
"form-data; name=\"$name\"; filename=\"$filename\""
328
329
/**
330
* Create Content-Disposition header for attachment
331
* @param filename File name
332
* @return Content-Disposition header value
333
*/
334
fun attachment(filename: String): String = "attachment; filename=\"$filename\""
335
}
336
```
337
338
**Usage Examples:**
339
340
```kotlin
341
import io.ktor.client.request.forms.*
342
import io.ktor.http.*
343
344
val formData = formData {
345
// Simple field
346
append("message", "Hello World", Headers.build {
347
append(HttpHeaders.ContentDisposition, ContentDisposition.formData("message"))
348
})
349
350
// File field
351
append("upload", fileBytes, Headers.build {
352
append(HttpHeaders.ContentDisposition,
353
ContentDisposition.formData("upload", "document.pdf"))
354
append(HttpHeaders.ContentType, "application/pdf")
355
})
356
}
357
```
358
359
### Parameters Building
360
361
Utilities for building URL-encoded form parameters.
362
363
```kotlin { .api }
364
/**
365
* Parameters interface for form data
366
*/
367
interface Parameters : StringValues {
368
companion object {
369
/** Empty parameters instance */
370
val Empty: Parameters
371
372
/**
373
* Build parameters using DSL
374
* @param block Building block
375
* @return Built parameters
376
*/
377
fun build(block: ParametersBuilder.() -> Unit): Parameters
378
}
379
}
380
381
/**
382
* Builder for constructing parameters
383
*/
384
interface ParametersBuilder : StringValuesBuilder {
385
/**
386
* Append parameter value
387
* @param name Parameter name
388
* @param value Parameter value
389
*/
390
fun append(name: String, value: String)
391
392
/**
393
* Append multiple values for parameter
394
* @param name Parameter name
395
* @param values Parameter values
396
*/
397
fun appendAll(name: String, values: Iterable<String>)
398
399
/**
400
* Set parameter value (replaces existing)
401
* @param name Parameter name
402
* @param value Parameter value
403
*/
404
fun set(name: String, value: String)
405
406
/**
407
* Remove parameter
408
* @param name Parameter name to remove
409
*/
410
fun remove(name: String)
411
412
/**
413
* Clear all parameters
414
*/
415
fun clear()
416
417
/**
418
* Build final parameters
419
* @return Immutable parameters instance
420
*/
421
fun build(): Parameters
422
}
423
```
424
425
**Usage Examples:**
426
427
```kotlin
428
import io.ktor.client.request.forms.*
429
import io.ktor.http.*
430
431
// Build parameters for form submission
432
val parameters = Parameters.build {
433
append("search", "kotlin")
434
append("category", "programming")
435
append("tags", "multiplatform")
436
append("tags", "coroutines") // Multiple values for same key
437
set("limit", "50") // Replace any existing value
438
}
439
440
val response = client.submitForm(
441
url = "https://api.example.com/search",
442
formParameters = parameters
443
)
444
```
445
446
## Platform-Specific Support
447
448
### JVM File Support
449
450
Additional support for File objects on JVM platform.
451
452
```kotlin { .api }
453
/**
454
* JVM-specific file content support
455
*/
456
class LocalFileContent(
457
private val file: File
458
) : OutgoingContent.ReadChannelContent() {
459
override val contentLength: Long = file.length()
460
override fun readFrom(): ByteReadChannel
461
}
462
463
/**
464
* Set request body from File (JVM only)
465
* @param file File to upload
466
*/
467
fun HttpRequestBuilder.setBody(file: File)
468
469
/**
470
* Append file to form data (JVM only)
471
* @param key Field name
472
* @param file File to append
473
* @param headers Custom headers
474
*/
475
fun FormBuilder.appendFile(
476
key: String,
477
file: File,
478
headers: Headers = Headers.Empty
479
)
480
```
481
482
**Usage Examples (JVM only):**
483
484
```kotlin
485
import io.ktor.client.*
486
import io.ktor.client.request.*
487
import io.ktor.client.request.forms.*
488
import java.io.File
489
490
val client = HttpClient()
491
492
// Upload file directly
493
val file = File("document.pdf")
494
val response = client.post("https://api.example.com/upload") {
495
setBody(file)
496
}
497
498
// Include file in form
499
val formWithFile = formData {
500
append("title", "Important Document")
501
appendFile("document", file, Headers.build {
502
append(HttpHeaders.ContentType, "application/pdf")
503
})
504
}
505
```
506
507
## Types
508
509
Form data related types:
510
511
```kotlin { .api }
512
/**
513
* String values interface for parameters
514
*/
515
interface StringValues {
516
val caseInsensitiveName: Boolean
517
518
fun get(name: String): String?
519
fun getAll(name: String): List<String>?
520
fun names(): Set<String>
521
fun isEmpty(): Boolean
522
fun entries(): Set<Map.Entry<String, List<String>>>
523
524
operator fun contains(name: String): Boolean
525
fun forEach(body: (String, List<String>) -> Unit)
526
}
527
528
/**
529
* Headers interface extending StringValues
530
*/
531
interface Headers : StringValues {
532
companion object {
533
val Empty: Headers
534
535
fun build(block: HeadersBuilder.() -> Unit): Headers
536
}
537
}
538
539
/**
540
* Headers builder interface
541
*/
542
interface HeadersBuilder : StringValuesBuilder {
543
fun append(name: String, value: String)
544
fun appendAll(name: String, values: Iterable<String>)
545
fun set(name: String, value: String)
546
fun remove(name: String)
547
fun clear()
548
fun build(): Headers
549
}
550
551
/**
552
* Generate boundary for multipart content
553
* @return Random boundary string
554
*/
555
fun generateBoundary(): String
556
557
/**
558
* Input stream interface for reading data
559
*/
560
interface Input : Closeable {
561
fun readByte(): Byte
562
fun readBytes(count: Int): ByteArray
563
fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int
564
}
565
```