-
-
Save jonasbark/3a0054dcf0c8ac9a896a0d805574914c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal class MultiPartContent(val parts: List<Part>) : OutgoingContent.WriteChannelContent() { | |
//val uuid = "RANDOM234" | |
//val time = System.currentTimeMillis() | |
//val time = "123134234234" | |
//val boundary = "***ktor-$uuid-ktor-$time***" | |
val boundary = generateBoundary() | |
private fun generateBoundary(): String = buildString { | |
repeat(32) { | |
append(Random.nextInt().absoluteValue.toString(16)) | |
} | |
}.take(70) | |
data class Part(val name: String, val size: Long, val filename: String? = null, val headers: Headers = Headers.Empty, val writer: suspend ByteWriteChannel.() -> Unit) | |
override suspend fun writeTo(channel: ByteWriteChannel) { | |
for (part in parts) { | |
channel.writeStringUtf8("--$boundary\r\n") | |
val partHeaders = Headers.build { | |
val fileNamePart = if (part.filename != null) "; filename=\"${part.filename}\"" else "" | |
append(HttpHeaders.ContentDisposition, "form-data; name=\"${part.name}\"$fileNamePart") | |
append(HttpHeaders.ContentLength, part.size.toString()) | |
appendAll(part.headers) | |
} | |
for ((key, value) in partHeaders.flattenEntries()) { | |
channel.writeStringUtf8("$key: $value\r\n") | |
} | |
channel.writeStringUtf8("\r\n") | |
part.writer(channel) | |
channel.writeStringUtf8("\r\n") | |
} | |
channel.writeStringUtf8("--$boundary--\r\n") | |
} | |
override val contentType = ContentType.MultiPart.FormData | |
.withParameter("boundary", boundary) | |
override val contentLength: Long? | |
get() { | |
var length = 0L | |
for (part in parts) { | |
length += ("--$boundary\r\n").length | |
val partHeaders = Headers.build { | |
val fileNamePart = if (part.filename != null) "; filename=\"${part.filename}\"" else "" | |
append(HttpHeaders.ContentDisposition, "form-data; name=\"${part.name}\"$fileNamePart") | |
append(HttpHeaders.ContentLength, part.size.toString()) | |
appendAll(part.headers) | |
} | |
for ((key, value) in partHeaders.flattenEntries()) { | |
length += ("$key: $value\r\n").length | |
} | |
length += ("\r\n").length | |
length += part.size | |
length += ("\r\n").length | |
} | |
length += ("--$boundary--\r\n").length | |
print("CONTENT LENGTH: $length") | |
return length | |
} | |
class Builder { | |
val parts = arrayListOf<Part>() | |
private fun add(part: Part) { | |
parts += part | |
} | |
private fun add(name: String, size: Long, filename: String? = null, contentType: ContentType? = null, headers: Headers = Headers.Empty, writer: suspend ByteWriteChannel.() -> Unit) { | |
val contentTypeHeaders: Headers = if (contentType != null) headersOf(HttpHeaders.ContentType, contentType.toString()) else headersOf() | |
add(Part(name, size, filename, headers + contentTypeHeaders, writer)) | |
} | |
fun add(name: String, size: Long, text: String, contentType: ContentType? = null, filename: String? = null) { | |
add(name, size, filename, contentType) { writeStringUtf8(text) } | |
} | |
fun add(name: String, size: Long, data: ByteArray, contentType: ContentType? = ContentType.Application.OctetStream, filename: String? = null) { | |
add(name, size, filename, contentType) { writeFully(data) } | |
} | |
internal fun build(): MultiPartContent = MultiPartContent(parts.toList()) | |
} | |
companion object { | |
fun build(callback: Builder.() -> Unit) = Builder().apply(callback).build() | |
} | |
} | |
private operator fun Headers.plus(other: Headers): Headers = when { | |
this.isEmpty() -> other | |
other.isEmpty() -> this | |
else -> Headers.build { | |
appendAll(this@plus) | |
appendAll(other) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment