CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-ktor--ktor

A multiplatform asynchronous framework for creating microservices, web applications, and HTTP clients written in Kotlin from the ground up

Pending
Overview
Eval results
Files

form-data.mddocs/

Form Data Handling

URL-encoded and multipart form data support for form submissions and file uploads with type-safe DSL builders.

Capabilities

FormDataContent Class

URL-encoded form data content for application/x-www-form-urlencoded submissions.

/**
 * Content for URL-encoded form data submissions
 */
class FormDataContent(
    val formData: Parameters
) : OutgoingContent.ByteArrayContent() {
    /** Content length in bytes */
    override val contentLength: Long
    
    /** Content type for URL-encoded form data */
    override val contentType: ContentType = ContentType.Application.FormUrlEncoded
    
    /**
     * Get form data as byte array
     * @return URL-encoded form data bytes
     */
    override fun bytes(): ByteArray
}

Usage Examples:

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.http.*

val client = HttpClient()

// Create form data content
val formData = Parameters.build {
    append("username", "john.doe")
    append("password", "secretpassword")
    append("remember", "true")
}

val response = client.post("https://api.example.com/login") {
    setBody(FormDataContent(formData))
}

MultiPartFormDataContent Class

Multipart form data content for file uploads and complex form submissions.

/**
 * Content for multipart form data submissions
 */
class MultiPartFormDataContent(
    parts: List<PartData>,
    val boundary: String = generateBoundary(),
    override val contentType: ContentType = ContentType.MultiPart.FormData
        .withParameter("boundary", boundary)
) : OutgoingContent.WriteChannelContent() {
    /** Content length, null for streaming */
    override var contentLength: Long? = null
    
    /**
     * Write multipart content to channel
     * @param channel Output channel for writing data
     */
    override suspend fun writeTo(channel: ByteWriteChannel)
}

Form Submission Functions

Convenient functions for submitting forms without manually creating content objects.

/**
 * Submit URL-encoded form data
 * @param url Target URL
 * @param formParameters Form parameters to submit
 * @param encodeInQuery Whether to encode parameters in URL query string
 * @param block Additional request configuration
 * @return HttpResponse from server
 */
suspend fun HttpClient.submitForm(
    url: String,
    formParameters: Parameters = Parameters.Empty,
    encodeInQuery: Boolean = false,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse

/**
 * Submit multipart form data with binary data
 * @param url Target URL
 * @param formData List of form parts including files
 * @param block Additional request configuration
 * @return HttpResponse from server
 */
suspend fun HttpClient.submitFormWithBinaryData(
    url: String,
    formData: List<PartData>,
    block: HttpRequestBuilder.() -> Unit = {}
): HttpResponse

Usage Examples:

import io.ktor.client.*
import io.ktor.client.request.forms.*
import io.ktor.http.*

val client = HttpClient()

// Submit simple form
val response = client.submitForm(
    url = "https://api.example.com/contact",
    formParameters = Parameters.build {
        append("name", "John Doe")
        append("email", "john@example.com")
        append("message", "Hello from Ktor!")
    }
)

// Submit form with file upload
val formData = formData {
    append("name", "John Doe")
    append("file", fileBytes, Headers.build {
        append(HttpHeaders.ContentDisposition, "filename=\"document.pdf\"")
        append(HttpHeaders.ContentType, "application/pdf")
    })
}

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

Form Building DSL

Type-safe DSL for building multipart form data with support for text fields, files, and binary data.

/**
 * Build form data using DSL
 * @param block Form building block
 * @return List of form parts
 */
fun formData(block: FormBuilder.() -> Unit): List<PartData>

/**
 * Builder for constructing multipart form data
 */
class FormBuilder {
    /**
     * Append text field to form
     * @param key Field name
     * @param value Field value
     */
    fun append(key: String, value: String)
    
    /**
     * Append text field with custom headers
     * @param key Field name
     * @param value Field value
     * @param headers Custom headers for the part
     */
    fun append(key: String, value: String, headers: Headers)
    
    /**
     * Append binary data to form
     * @param key Field name
     * @param value Binary data as byte array
     * @param headers Custom headers for the part
     */
    fun append(key: String, value: ByteArray, headers: Headers = Headers.Empty)
    
    /**
     * Append data from Input provider
     * @param key Field name
     * @param provider Function that provides Input stream
     * @param headers Custom headers for the part
     */
    fun append(
        key: String,
        provider: () -> Input,
        headers: Headers = Headers.Empty
    )
    
    /**
     * Append data from ByteReadChannel provider
     * @param key Field name
     * @param provider Function that provides ByteReadChannel
     * @param headers Custom headers for the part
     */
    fun append(
        key: String,
        provider: () -> ByteReadChannel,
        headers: Headers = Headers.Empty
    )
}

Usage Examples:

import io.ktor.client.*
import io.ktor.client.request.forms.*
import io.ktor.http.*
import io.ktor.utils.io.*
import java.io.File

val client = HttpClient()

// Build complex form with various field types
val formData = formData {
    // Text fields
    append("username", "john.doe")
    append("description", "User profile update")
    
    // File upload from byte array
    append("avatar", avatarBytes, Headers.build {
        append(HttpHeaders.ContentDisposition, "filename=\"avatar.jpg\"")
        append(HttpHeaders.ContentType, "image/jpeg")
    })
    
    // File upload from File (JVM)
    append("document", File("document.pdf").readBytes(), Headers.build {
        append(HttpHeaders.ContentDisposition, "filename=\"document.pdf\"")
        append(HttpHeaders.ContentType, "application/pdf")
    })
    
    // Streaming data
    append("data", { ByteReadChannel(largeDataBytes) }, Headers.build {
        append(HttpHeaders.ContentDisposition, "filename=\"data.bin\"")
        append(HttpHeaders.ContentType, "application/octet-stream")
    })
}

val response = client.submitFormWithBinaryData(
    url = "https://api.example.com/profile/update",
    formData = formData
) {
    header("Authorization", "Bearer $token")
}

PartData Types

Data classes representing different types of form parts.

/**
 * Base class for form parts
 */
sealed class PartData {
    /** Headers for this part */
    abstract val headers: Headers
    
    /** Release resources associated with this part */
    abstract fun dispose()
}

/**
 * Form part containing text data
 */
class PartData.FormItem(
    val value: String,
    override val headers: Headers
) : PartData()

/**
 * Form part containing binary data
 */
class PartData.BinaryItem(
    val provider: () -> Input,
    override val headers: Headers
) : PartData()

/**
 * Form part containing binary channel data
 */
class PartData.BinaryChannelItem(
    val provider: () -> ByteReadChannel,
    override val headers: Headers
) : PartData()

/**
 * Form part containing file data
 */
class PartData.FileItem(
    val provider: () -> Input,
    override val headers: Headers
) : PartData()

Content-Disposition Headers

Utilities for working with Content-Disposition headers in form data.

/**
 * Content-Disposition header utilities
 */
object ContentDisposition {
    /**
     * Create Content-Disposition header for form field
     * @param name Field name
     * @return Content-Disposition header value
     */
    fun formData(name: String): String = "form-data; name=\"$name\""
    
    /**
     * Create Content-Disposition header for file field
     * @param name Field name
     * @param filename File name
     * @return Content-Disposition header value
     */
    fun formData(name: String, filename: String): String = 
        "form-data; name=\"$name\"; filename=\"$filename\""
    
    /**
     * Create Content-Disposition header for attachment
     * @param filename File name
     * @return Content-Disposition header value
     */
    fun attachment(filename: String): String = "attachment; filename=\"$filename\""
}

Usage Examples:

import io.ktor.client.request.forms.*
import io.ktor.http.*

val formData = formData {
    // Simple field
    append("message", "Hello World", Headers.build {
        append(HttpHeaders.ContentDisposition, ContentDisposition.formData("message"))
    })
    
    // File field
    append("upload", fileBytes, Headers.build {
        append(HttpHeaders.ContentDisposition, 
               ContentDisposition.formData("upload", "document.pdf"))
        append(HttpHeaders.ContentType, "application/pdf")
    })
}

Parameters Building

Utilities for building URL-encoded form parameters.

/**
 * Parameters interface for form data
 */
interface Parameters : StringValues {
    companion object {
        /** Empty parameters instance */
        val Empty: Parameters
        
        /**
         * Build parameters using DSL
         * @param block Building block
         * @return Built parameters
         */
        fun build(block: ParametersBuilder.() -> Unit): Parameters
    }
}

/**
 * Builder for constructing parameters
 */
interface ParametersBuilder : StringValuesBuilder {
    /**
     * Append parameter value
     * @param name Parameter name
     * @param value Parameter value
     */
    fun append(name: String, value: String)
    
    /**
     * Append multiple values for parameter
     * @param name Parameter name
     * @param values Parameter values
     */
    fun appendAll(name: String, values: Iterable<String>)
    
    /**
     * Set parameter value (replaces existing)
     * @param name Parameter name
     * @param value Parameter value
     */
    fun set(name: String, value: String)
    
    /**
     * Remove parameter
     * @param name Parameter name to remove
     */
    fun remove(name: String)
    
    /**
     * Clear all parameters
     */
    fun clear()
    
    /**
     * Build final parameters
     * @return Immutable parameters instance
     */
    fun build(): Parameters
}

Usage Examples:

import io.ktor.client.request.forms.*
import io.ktor.http.*

// Build parameters for form submission
val parameters = Parameters.build {
    append("search", "kotlin")
    append("category", "programming")
    append("tags", "multiplatform")
    append("tags", "coroutines")  // Multiple values for same key
    set("limit", "50")            // Replace any existing value
}

val response = client.submitForm(
    url = "https://api.example.com/search",
    formParameters = parameters
)

Platform-Specific Support

JVM File Support

Additional support for File objects on JVM platform.

/**
 * JVM-specific file content support
 */
class LocalFileContent(
    private val file: File
) : OutgoingContent.ReadChannelContent() {
    override val contentLength: Long = file.length()
    override fun readFrom(): ByteReadChannel
}

/**
 * Set request body from File (JVM only)
 * @param file File to upload
 */
fun HttpRequestBuilder.setBody(file: File)

/**
 * Append file to form data (JVM only)
 * @param key Field name
 * @param file File to append
 * @param headers Custom headers
 */
fun FormBuilder.appendFile(
    key: String,
    file: File,
    headers: Headers = Headers.Empty
)

Usage Examples (JVM only):

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import java.io.File

val client = HttpClient()

// Upload file directly
val file = File("document.pdf")
val response = client.post("https://api.example.com/upload") {
    setBody(file)
}

// Include file in form
val formWithFile = formData {
    append("title", "Important Document")
    appendFile("document", file, Headers.build {
        append(HttpHeaders.ContentType, "application/pdf")
    })
}

Types

Form data related types:

/**
 * String values interface for parameters
 */
interface StringValues {
    val caseInsensitiveName: Boolean
    
    fun get(name: String): String?
    fun getAll(name: String): List<String>?
    fun names(): Set<String>
    fun isEmpty(): Boolean
    fun entries(): Set<Map.Entry<String, List<String>>>
    
    operator fun contains(name: String): Boolean
    fun forEach(body: (String, List<String>) -> Unit)
}

/**
 * Headers interface extending StringValues
 */
interface Headers : StringValues {
    companion object {
        val Empty: Headers
        
        fun build(block: HeadersBuilder.() -> Unit): Headers
    }
}

/**
 * Headers builder interface
 */
interface HeadersBuilder : StringValuesBuilder {
    fun append(name: String, value: String)
    fun appendAll(name: String, values: Iterable<String>)
    fun set(name: String, value: String)
    fun remove(name: String)
    fun clear()
    fun build(): Headers
}

/**
 * Generate boundary for multipart content
 * @return Random boundary string
 */
fun generateBoundary(): String

/**
 * Input stream interface for reading data
 */
interface Input : Closeable {
    fun readByte(): Byte
    fun readBytes(count: Int): ByteArray
    fun readAvailable(dst: ByteArray, offset: Int, length: Int): Int
}

Install with Tessl CLI

npx tessl i tessl/maven-io-ktor--ktor

docs

client-management.md

engine-system.md

form-data.md

http-utilities.md

index.md

plugin-system.md

request-operations.md

response-handling.md

routing-system.md

server-framework.md

tile.json