Supported Platforms for MultipartKit
mainMultipartKit requires Swift 6.1 or later and supports:
- All Linux distributions supported by Swift 6.1+
- macOS 10.15+
- iOS 13+
- tvOS 13+
- watchOS 6+
- visionOS 1+
repository·main·Indexed 41 days ago
https://github.com/vapor/multipart-kitA Swift library for encoding and decoding multipart/form-data. It provides Codable support via FormDataEncoder and FormDataDecoder, manual parsing with MultipartParser, and memory-efficient processing using StreamingMultipartParserAsyncSequence and MultipartWriter. Requires Swift 6.1 or later.
MultipartKit requires Swift 6.1 or later and supports:
To parse large multipart messages without loading the entire message into memory, use StreamingMultipartParserAsyncSequence. This wraps an AsyncSequence of body chunks and yields MultipartSection objects as soon as they are parsed.
MultipartSection can be:
.headerFields(let fields): Emitted when headers for a part are parsed..bodyChunk(let chunk): Emitted for each chunk of the part's body..boundary(let end): Emitted when a boundary is reached (where end is true if the message is finished).let sequence = StreamingMultipartParserAsyncSequence(boundary: boundary, buffer: stream)
for try await section in sequence {
switch section {
case .headerFields(let fields):
print(fields[.contentDisposition] ?? "")
case .bodyChunk(let chunk):
try await file.write(contentsOf: chunk)
case .boundary(let end):
print(end ? "message finished" : "part finished")
}
}MultipartKit provides FormDataEncoder and FormDataDecoder to handle multipart/form-data using Swift's Codable protocol.
Use FormDataEncoder().encode(_:boundary:) to convert a Codable instance into a multipart message. You can encode to a String, a specific body type (like [UInt8]), or append directly into an existing buffer.
Use FormDataDecoder().decode(_:from:boundary:) to reconstruct a Codable instance from a multipart message. The decoder accepts a String or any MultipartPartBodyElement.
Note: null values are not supported for encoding or decoding as there is no standard representation for null in Multipart.
Nesting and Collections: Nested structures are represented using square bracket notation for paths (e.g., nested[0][tag]). Array elements always include their index to support complex nesting.
struct User: Codable {
let name: String
let email: String
}
let user = User(name: "Ed", email: "ed@example.com")
let boundary = "abc123"
// Encoding to String
let encoded = try FormDataEncoder().encode(user, boundary: boundary)
// Decoding from String
let decoded = try FormDataDecoder().decode(User.self, from: encoded, boundary: boundary)
// Encoding to [UInt8]
let bytes: [UInt8] = try FormDataEncoder().encode(user, boundary: boundary)
// Appending to an existing buffer
var buffer: [UInt8] = []
try FormDataEncoder().encode(user, boundary: boundary, into: &buffer)Add MultipartKit to your Swift Package Manager dependencies using the following steps:
Package.swift dependencies:dependencies: [
// ...
.package(url: "https://github.com/vapor/multipart-kit.git", from: "5.0.0-alpha.5"),
]targets: [
.target(name: "MyAppTarget", dependencies: [
// ...
.product(name: "MultipartKit", package: "multipart-kit"),
])
]dependencies: [
// ...
.package(url: "https://github.com/vapor/multipart-kit.git", from: "5.0.0-alpha.5"),
]
targets: [
.target(name: "MyAppTarget", dependencies: [
// ...
.product(name: "MultipartKit", package: "multipart-kit"),
])
]If you do not require Codable support, you can manually parse messages using MultipartParser. This allows you to iterate over MultipartPart objects, accessing their headerFields and body directly.
MultipartPart provides a ContentDisposition object which simplifies access to dispositionType, name, filename, and additionalParameters without manual header parsing.
let parts = try MultipartParser<ArraySlice<UInt8>>(boundary: boundary).parse(message)
for part in parts {
// Access part.headerFields and part.body
// Use part.contentDisposition to get name, filename, etc.
}Serialization is handled via the MultipartWriter protocol.
MemoryMultipartWriter is a built-in implementation that collects the entire message in memory. Use writePart(_:) to add parts and finish() to complete the message, then retrieve the result with getResult().
To write to a destination other than memory (e.g., a file or socket), conform your type to MultipartWriter. You only need to implement write(bytes:); boundaries and headers are handled automatically.
StreamingMultipartWriterAsyncSequence allows you to turn an AsyncSequence of MultipartSections into an AsyncSequence of serialized chunks, which is useful for streaming outbound data.
// Using MemoryMultipartWriter
var writer = MemoryMultipartWriter<[UInt8]>(boundary: boundary)
try await writer.writePart(
MultipartPart(
headerFields: [.contentDisposition: #"form-data; name=\"file\"; filename=\"hello.txt\""#],
body: Array("Hello, world!".utf8)
)
)
try await writer.finish()
let serialized = writer.getResult()
// Using StreamingMultipartWriterAsyncSequence
let sequence = StreamingMultipartWriterAsyncSequence(
backingSequence: sections,
boundary: boundary,
outboundBody: ArraySlice<UInt8>.self
)
for try await chunk in sequence {
try await socket.write(chunk)
}