Dirk
04/25/2023, 4:01 PMDirk
04/25/2023, 4:02 PMval file = (dokument as File)
val blob = dokument as Blob
val fg = httpClient.call {
post(url) {
header(HttpHeaders.ContentType, ContentType.MultiPart.FormData)
setBody(
MultiPartFormDataContent(
formData {
append("datei", file, Headers.build {
append(HttpHeaders.ContentType, dokument.type)
})
},
boundary = "WebAppBoundary"
)
)
onUpload { bytesSentTotal, contentLength -> log.debug { "Es wurden bereits $bytesSentTotal von $contentLength Bytes hochgeladen" } }
}
}
Dirk
04/25/2023, 4:03 PMAleksei Tirman [JB]
04/26/2023, 8:28 AMFileReader
to read a file to an ArrayBuffer
and then convert it to a ByteArray
using the Int8Array
constructor. Here is an example:
val client = HttpClient(Js) {}
val fileField = document.getElementById("file")
document.getElementById("form")!!.addEventListener("submit", { e ->
e.preventDefault()
val file = (fileField as HTMLInputElement).files!![0]!!
val reader = FileReader()
reader.readAsArrayBuffer(file)
reader.onload = {
val buffer = reader.result as ArrayBuffer
GlobalScope.launch {
val r = <http://client.post|client.post>("<https://httpbin.org/post>") {
setBody(
MultiPartFormDataContent(
formData {
append("datei", Int8Array(buffer, 0, buffer.byteLength).asDynamic() as ByteArray, Headers.build {
append(HttpHeaders.ContentType, "application/octet-stream")
})
},
boundary = "WebAppBoundary"
)
)
}
println("Response: ${r.bodyAsText()}")
}
}
})
HTML:
<form id="form">
<input type="file" id="file">
<input type="submit" value="Send">
</form>
Aleksei Tirman [JB]
04/26/2023, 8:35 AMArrayBuffer
then use the following line:
Int8Array(buffer, 0, buffer.byteLength).asDynamic() as ByteArray
Dirk
04/26/2023, 9:59 AMDirk
04/26/2023, 10:00 AMDirk
04/26/2023, 10:01 AMval multipartData = call.receiveMultipart()
Either.catch {
multipartData.forEachPart { data ->
when (data) {
is FormItem -> <http://log.info|log.info>("FormItem: ${data.name} -> ${data.value}")
is FileItem -> {
val dateiname = data.originalFileName ?: "gutachten_${Random.nextLong()}.pdf"
val contentLength = call.request.header(HttpHeaders.ContentLength)
?.toIntOrNull()
.validiereNichtNull {
DateiGroesseNichtAngegeben("In dem FileItem ist keine Größe hinterlegt oder diese ist falsch konfiguriert ($data")
}
.validiere({ it <= fgEinstellungen.maxSizeUpload }) {
HochgeladeneDateiIstZuGross("Das hochgeladene FG ist zu große. Maximal erlaubt sind 10 MB ($it > ${fgEinstellungen.maxSizeUpload}")
}
val contentType = data.contentType
.validiereNichtNull {
ContenttypNichtHinterlegt("Beim Upload fehlt der ContentTyp ($data)")
}
.validiere({ it == ContentType.fromFileExtension(".pdf").first() }) {
UnzulaessigerDateityp("Zulässig für den FG-Upload sind nur PDFs. (aktuell: $dateiname -> $contentType")
}
// FIXME temp Name
val tmpFile = File(tmpDir, "$dateiname")
data.streamProvider().transferTo(tmpFile.outputStream())
<http://log.info|log.info>("Datei $dateiname ($contentType -> $contentLength) wurde hochgeladen")
tmpFile.delete()
}
else -> raise(UngueltigerFormtyp("Der Upload unterstützt nur FormItem und FileItem"))
}
data.dispose
}
}
Dirk
04/26/2023, 10:03 AM2023-04-26 11:53:34.633 [eventLoopGroupProxy-4-6] ppd/jce22478=a3p1oau63mhm5f+jr ERROR ktor.application - Input length = 1
io.ktor.utils.io.charsets.MalformedInputException: Input length = 1
at io.ktor.utils.io.charsets.CharsetJVMKt.throwExceptionWrapped(CharsetJVM.kt:325)
at io.ktor.utils.io.charsets.CharsetJVMKt.decodeImplSlow(CharsetJVM.kt:289)
at io.ktor.utils.io.charsets.CharsetJVMKt.decodeExactBytes(CharsetJVM.kt:254)
at io.ktor.utils.io.core.StringsKt.readTextExactBytes(Strings.kt:282)
at io.ktor.utils.io.core.StringsKt.readTextExactBytes$default(Strings.kt:281)
at io.ktor.utils.io.core.Input.readText(Input.kt:411)
at io.ktor.utils.io.core.Input.readText$default(Input.kt:408)
at io.ktor.http.cio.CIOMultipartDataBase.partToData(CIOMultipartDataBase.kt:77)
at io.ktor.http.cio.CIOMultipartDataBase.access$partToData(CIOMultipartDataBase.kt:21)
at io.ktor.http.cio.CIOMultipartDataBase$partToData$1.invokeSuspend(CIOMultipartDataBase.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
...
Aleksei Tirman [JB]
04/26/2023, 12:45 PMDirk
04/26/2023, 1:02 PMval multipartData = call.receiveMultipart()
multipartData.forEachPart { data ->
when (data) {
is FormItem -> <http://log.info|log.info>("FormItem: ${data.name} -> ${data.value}")
is FileItem -> {
val tmpFile = File(tmpDir, "$dateiname")
data.streamProvider().transferTo(tmpFile.outputStream())
......
}
Aleksei Tirman [JB]
04/28/2023, 7:02 AMfilename
parameter for a binary part.