or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-configuration.mdcookie-management.mdforms-and-uploads.mdhttp-caching.mdhttp-requests.mdindex.mdplugin-system.mdresponse-handling.mdserver-sent-events.mdwebsockets.md

forms-and-uploads.mddocs/

0

# Form Data and File Uploads

1

2

Comprehensive form submission and file upload functionality including URL-encoded forms, multipart forms, and binary data handling.

3

4

## Capabilities

5

6

### Form Submission Functions

7

8

Submit forms with URL-encoded and multipart data formats.

9

10

```kotlin { .api }

11

/**

12

* Submit URL-encoded form data

13

* @param url Target URL

14

* @param formParameters Form fields as Parameters

15

* @param encodeInQuery Whether to encode parameters in query string (GET)

16

* @returns HttpResponse

17

*/

18

suspend fun HttpClient.submitForm(

19

url: String,

20

formParameters: Parameters = Parameters.Empty,

21

encodeInQuery: Boolean = false

22

): HttpResponse

23

24

/**

25

* Submit multipart form data with binary support

26

* @param url Target URL

27

* @param formData List of form parts including files

28

* @returns HttpResponse

29

*/

30

suspend fun HttpClient.submitFormWithBinaryData(

31

url: String,

32

formData: List<PartData>

33

): HttpResponse

34

```

35

36

**Usage Examples:**

37

38

```kotlin

39

val client = HttpClient()

40

41

// Submit simple form

42

val response = client.submitForm(

43

url = "https://example.com/login",

44

formParameters = Parameters.build {

45

append("username", "john")

46

append("password", "secret")

47

append("remember", "true")

48

}

49

)

50

51

// Submit form as GET request with query parameters

52

val searchResponse = client.submitForm(

53

url = "https://example.com/search",

54

formParameters = Parameters.build {

55

append("q", "kotlin")

56

append("type", "code")

57

},

58

encodeInQuery = true

59

)

60

61

// Submit multipart form with file

62

val uploadResponse = client.submitFormWithBinaryData(

63

url = "https://example.com/upload",

64

formData = formData {

65

append("description", "Profile photo")

66

append("category", "image")

67

append("file", File("photo.jpg").readBytes(), Headers.build {

68

append(HttpHeaders.ContentType, "image/jpeg")

69

append(HttpHeaders.ContentDisposition, """form-data; name="file"; filename="photo.jpg"""")

70

})

71

}

72

)

73

```

74

75

### Form Data Content Classes

76

77

Content classes for different form data types.

78

79

```kotlin { .api }

80

/**

81

* URL-encoded form data content

82

*/

83

class FormDataContent(

84

val formData: Parameters

85

) : OutgoingContent.ByteArrayContent() {

86

override val contentType: ContentType

87

override val contentLength: Long?

88

override fun bytes(): ByteArray

89

}

90

91

/**

92

* Multipart form data content

93

*/

94

class MultiPartFormDataContent(

95

val parts: List<PartData>,

96

val boundary: String = generateBoundary()

97

) : OutgoingContent.WriteChannelContent() {

98

override val contentType: ContentType

99

override suspend fun writeTo(channel: ByteWriteChannel)

100

}

101

```

102

103

**Usage Examples:**

104

105

```kotlin

106

// Create form data content manually

107

val formContent = FormDataContent(Parameters.build {

108

append("name", "John Doe")

109

append("email", "john@example.com")

110

})

111

112

val response = client.post("https://example.com/users") {

113

setBody(formContent)

114

}

115

116

// Create multipart content manually

117

val multipartContent = MultiPartFormDataContent(

118

parts = listOf(

119

PartData.FormItem("John Doe", dispose = {}, partHeaders = Headers.build {

120

append(HttpHeaders.ContentDisposition, """form-data; name="name"""")

121

}),

122

PartData.FileItem(

123

provider = { File("document.pdf").readChannel() },

124

dispose = {},

125

partHeaders = Headers.build {

126

append(HttpHeaders.ContentType, "application/pdf")

127

append(HttpHeaders.ContentDisposition, """form-data; name="file"; filename="document.pdf"""")

128

}

129

)

130

)

131

)

132

133

val uploadResponse = client.post("https://example.com/upload") {

134

setBody(multipartContent)

135

}

136

```

137

138

### Form Data Builder

139

140

DSL for building form data with type safety and convenience methods.

141

142

```kotlin { .api }

143

/**

144

* Build form data using DSL

145

* @param block Form building block

146

* @returns List of PartData for multipart submission

147

*/

148

fun formData(block: FormBuilder.() -> Unit): List<PartData>

149

150

/**

151

* Form builder DSL class

152

*/

153

class FormBuilder {

154

/**

155

* Append text field

156

* @param key Field name

157

* @param value Field value

158

* @param headers Optional headers for this part

159

*/

160

fun append(key: String, value: String, headers: Headers = Headers.Empty)

161

162

/**

163

* Append binary data

164

* @param key Field name

165

* @param value Binary data

166

* @param headers Headers including content type and disposition

167

*/

168

fun append(key: String, value: ByteArray, headers: Headers = Headers.Empty)

169

170

/**

171

* Append file input stream

172

* @param key Field name

173

* @param value Input provider function

174

* @param headers Headers including content type and disposition

175

*/

176

fun append(key: String, value: () -> ByteReadChannel, headers: Headers = Headers.Empty)

177

178

/**

179

* Append channel content

180

* @param key Field name

181

* @param value Channel with content

182

* @param headers Headers including content type and disposition

183

*/

184

fun appendInput(key: String, value: ByteReadChannel, headers: Headers = Headers.Empty)

185

186

/**

187

* Append Input object (unified input abstraction)

188

* @param key Field name

189

* @param value Input object

190

* @param headers Headers including content type and disposition

191

*/

192

fun appendInput(key: String, value: Input, headers: Headers = Headers.Empty)

193

}

194

```

195

196

**Usage Examples:**

197

198

```kotlin

199

// Build complex form data

200

val formData = formData {

201

// Text fields

202

append("name", "John Doe")

203

append("email", "john@example.com")

204

append("age", "30")

205

206

// File from bytes

207

val imageBytes = File("profile.jpg").readBytes()

208

append("profile_image", imageBytes, Headers.build {

209

append(HttpHeaders.ContentType, "image/jpeg")

210

append(HttpHeaders.ContentDisposition, """form-data; name="profile_image"; filename="profile.jpg"""")

211

})

212

213

// File from stream provider

214

append("document", { File("report.pdf").readChannel() }, Headers.build {

215

append(HttpHeaders.ContentType, "application/pdf")

216

append(HttpHeaders.ContentDisposition, """form-data; name="document"; filename="report.pdf"""")

217

})

218

219

// Input stream

220

val dataStream = ByteReadChannel("large data content")

221

appendInput("data_file", dataStream, Headers.build {

222

append(HttpHeaders.ContentType, "text/plain")

223

append(HttpHeaders.ContentDisposition, """form-data; name="data_file"; filename="data.txt"""")

224

})

225

}

226

227

val response = client.submitFormWithBinaryData("https://example.com/upload", formData)

228

```

229

230

### PartData Sealed Class

231

232

Sealed class hierarchy for different types of form parts.

233

234

```kotlin { .api }

235

/**

236

* Base class for form parts

237

*/

238

sealed class PartData {

239

/** Headers for this part */

240

abstract val headers: Headers

241

242

/** Cleanup function */

243

abstract val dispose: () -> Unit

244

245

/**

246

* Text form field

247

*/

248

data class FormItem(

249

val value: String,

250

override val dispose: () -> Unit,

251

override val headers: Headers = Headers.Empty

252

) : PartData()

253

254

/**

255

* File form field with streaming content

256

*/

257

data class FileItem(

258

val provider: () -> ByteReadChannel,

259

override val dispose: () -> Unit,

260

override val headers: Headers = Headers.Empty

261

) : PartData()

262

263

/**

264

* Binary data form field

265

*/

266

data class BinaryItem(

267

val provider: () -> ByteArray,

268

override val dispose: () -> Unit,

269

override val headers: Headers = Headers.Empty

270

) : PartData()

271

272

/**

273

* Channel-based form field

274

*/

275

data class BinaryChannelItem(

276

val provider: () -> ByteReadChannel,

277

override val dispose: () -> Unit,

278

override val headers: Headers = Headers.Empty

279

) : PartData()

280

}

281

```

282

283

### Parameters Builder

284

285

Builder for URL-encoded form parameters.

286

287

```kotlin { .api }

288

/**

289

* Parameters collection interface

290

*/

291

interface Parameters : StringValues {

292

companion object {

293

val Empty: Parameters

294

295

/**

296

* Build parameters using DSL

297

* @param block Parameter building block

298

* @returns Parameters collection

299

*/

300

fun build(block: ParametersBuilder.() -> Unit): Parameters

301

}

302

}

303

304

/**

305

* Parameters builder class

306

*/

307

class ParametersBuilder : StringValuesBuilder() {

308

/**

309

* Append parameter value

310

* @param name Parameter name

311

* @param value Parameter value

312

*/

313

override fun append(name: String, value: String)

314

315

/**

316

* Append all parameters from another collection

317

* @param parameters Source parameters

318

*/

319

fun appendAll(parameters: Parameters)

320

321

/**

322

* Append parameter with multiple values

323

* @param name Parameter name

324

* @param values Parameter values

325

*/

326

fun appendAll(name: String, values: Iterable<String>)

327

328

/**

329

* Set parameter value (replaces existing)

330

* @param name Parameter name

331

* @param value Parameter value

332

*/

333

override fun set(name: String, value: String)

334

335

/**

336

* Build final Parameters collection

337

* @returns Immutable Parameters

338

*/

339

override fun build(): Parameters

340

}

341

```

342

343

**Usage Examples:**

344

345

```kotlin

346

// Build parameters for form submission

347

val params = Parameters.build {

348

append("username", "john")

349

append("email", "john@example.com")

350

append("interests", "kotlin")

351

append("interests", "programming") // Multiple values for same key

352

353

// Conditional parameters

354

if (rememberMe) {

355

append("remember", "true")

356

}

357

358

// Append from map

359

val additionalParams = mapOf("source" to "mobile", "version" to "1.0")

360

additionalParams.forEach { (key, value) ->

361

append(key, value)

362

}

363

}

364

365

val response = client.submitForm("https://example.com/register", params)

366

367

// Access parameter values

368

val username = params["username"] // Single value

369

val interests = params.getAll("interests") // All values for key

370

val allNames = params.names() // All parameter names

371

```

372

373

### Content-Type and Headers

374

375

Utilities for setting proper content types and headers for form data.

376

377

```kotlin { .api }

378

/**

379

* Generate multipart boundary string

380

* @returns Random boundary string

381

*/

382

fun generateBoundary(): String

383

384

/**

385

* Create Content-Disposition header for form field

386

* @param name Field name

387

* @param filename Optional filename

388

* @returns Content-Disposition header value

389

*/

390

fun formDataContentDisposition(name: String, filename: String? = null): String

391

392

/**

393

* Common content types for file uploads

394

*/

395

object ContentTypes {

396

val MultipartFormData: ContentType

397

val ApplicationFormUrlEncoded: ContentType

398

val ApplicationOctetStream: ContentType

399

val TextPlain: ContentType

400

val ImageJpeg: ContentType

401

val ImagePng: ContentType

402

val ApplicationPdf: ContentType

403

}

404

```

405

406

**Usage Examples:**

407

408

```kotlin

409

// Custom headers for file upload

410

val headers = Headers.build {

411

append(HttpHeaders.ContentType, "image/png")

412

append(HttpHeaders.ContentDisposition, formDataContentDisposition("avatar", "profile.png"))

413

append("X-Upload-Source", "mobile-app")

414

}

415

416

val formData = formData {

417

append("user_id", "123")

418

append("avatar", File("profile.png").readBytes(), headers)

419

}

420

421

// Manual content type handling

422

val response = client.post("https://example.com/upload") {

423

setBody(MultiPartFormDataContent(formData))

424

// Content-Type with boundary is set automatically

425

}

426

```

427

428

## Types

429

430

### Form Data Types

431

432

```kotlin { .api }

433

/**

434

* String values collection interface

435

*/

436

interface StringValues {

437

val caseInsensitiveName: Boolean

438

439

fun get(name: String): String?

440

fun getAll(name: String): List<String>?

441

fun names(): Set<String>

442

fun isEmpty(): Boolean

443

fun entries(): Set<Map.Entry<String, List<String>>>

444

fun forEach(body: (String, List<String>) -> Unit)

445

446

operator fun contains(name: String): Boolean

447

operator fun contains(name: String, value: String): Boolean

448

}

449

450

/**

451

* Headers collection

452

*/

453

interface Headers : StringValues {

454

companion object {

455

val Empty: Headers

456

457

fun build(block: HeadersBuilder.() -> Unit): Headers

458

}

459

}

460

461

/**

462

* HTTP header names constants

463

*/

464

object HttpHeaders {

465

const val ContentType = "Content-Type"

466

const val ContentLength = "Content-Length"

467

const val ContentDisposition = "Content-Disposition"

468

const val ContentEncoding = "Content-Encoding"

469

const val Authorization = "Authorization"

470

const val UserAgent = "User-Agent"

471

const val Accept = "Accept"

472

const val AcceptEncoding = "Accept-Encoding"

473

const val AcceptLanguage = "Accept-Language"

474

const val CacheControl = "Cache-Control"

475

const val Connection = "Connection"

476

const val Cookie = "Cookie"

477

const val SetCookie = "Set-Cookie"

478

}

479

```

480

481

### File and Input Types

482

483

```kotlin { .api }

484

/**

485

* Input abstraction for different data sources

486

*/

487

interface Input : Closeable {

488

suspend fun readPacket(size: Int): ByteReadPacket

489

suspend fun readBuffer(size: Int): Buffer

490

suspend fun discard(max: Long): Long

491

suspend fun close()

492

}

493

494

/**

495

* Channel for reading bytes

496

*/

497

interface ByteReadChannel {

498

val availableForRead: Int

499

val isClosedForRead: Boolean

500

val isClosedForWrite: Boolean

501

val totalBytesRead: Long

502

503

suspend fun readByte(): Byte

504

suspend fun readPacket(size: Int): ByteReadPacket

505

suspend fun readRemaining(limit: Long = Long.MAX_VALUE): ByteReadPacket

506

suspend fun readBuffer(size: Int): Buffer

507

suspend fun discard(max: Long = Long.MAX_VALUE): Long

508

suspend fun cancel(cause: Throwable?)

509

}

510

511

/**

512

* Channel for writing bytes

513

*/

514

interface ByteWriteChannel {

515

val availableForWrite: Int

516

val isClosedForWrite: Boolean

517

val totalBytesWritten: Long

518

519

suspend fun writeByte(b: Byte)

520

suspend fun writePacket(packet: ByteReadPacket)

521

suspend fun writeBuffer(buffer: Buffer): Int

522

suspend fun flush()

523

suspend fun close(cause: Throwable?)

524

}

525

```