diff --git a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt index fe3f32a9b41..91b524c7f2b 100644 --- a/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt +++ b/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/client/UniUpload.kt @@ -1,15 +1,18 @@ package dev.inmo.micro_utils.ktor.client import dev.inmo.micro_utils.common.FileName +import dev.inmo.micro_utils.ktor.common.LambdaInputProvider import io.ktor.client.HttpClient import io.ktor.http.Headers import io.ktor.utils.io.core.Input import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.StringFormat +import kotlinx.serialization.json.Json data class UniUploadFileInfo( val fileName: FileName, - val bytesAllocator: Input + val mimeType: String, + val inputAllocator: LambdaInputProvider ) /** @@ -22,7 +25,7 @@ data class UniUploadFileInfo( expect suspend fun HttpClient.uniupload( url: String, data: Map, - stringFormat: StringFormat, resultDeserializer: DeserializationStrategy, - headers: Headers = Headers.Empty + headers: Headers = Headers.Empty, + stringFormat: StringFormat = Json.Default ): T? diff --git a/ktor/client/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualMPPFileInputProvider.kt b/ktor/client/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualMPPFileInputProvider.kt index 1b7790568f3..ad8a9ee406a 100644 --- a/ktor/client/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualMPPFileInputProvider.kt +++ b/ktor/client/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualMPPFileInputProvider.kt @@ -4,6 +4,8 @@ import dev.inmo.micro_utils.common.MPPFile import io.ktor.client.request.forms.InputProvider import io.ktor.utils.io.streams.asInput -actual suspend fun MPPFile.inputProvider(): InputProvider = InputProvider(length()) { +fun MPPFile.inputProviderSync(): InputProvider = InputProvider(length()) { inputStream().asInput() } + +actual suspend fun MPPFile.inputProvider(): InputProvider = inputProviderSync() diff --git a/ktor/client/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt b/ktor/client/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt new file mode 100644 index 00000000000..ed394e21075 --- /dev/null +++ b/ktor/client/src/jvmMain/kotlin/dev/inmo/micro_utils/ktor/client/ActualUniUpload.kt @@ -0,0 +1,88 @@ +package dev.inmo.micro_utils.ktor.client + +import dev.inmo.micro_utils.ktor.common.input +import io.ktor.client.HttpClient +import io.ktor.client.request.forms.InputProvider +import io.ktor.client.request.forms.formData +import io.ktor.client.request.forms.submitForm +import io.ktor.client.request.forms.submitFormWithBinaryData +import io.ktor.client.statement.bodyAsText +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode +import io.ktor.http.Parameters +import io.ktor.http.content.PartData +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.StringFormat +import kotlinx.serialization.encodeToString +import kotlinx.serialization.serializer +import java.io.File + +/** + * Will execute submitting of multipart data request + * + * @param data [Map] where keys will be used as names for multipart parts and values as values. If you will pass + * [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value + * in case you wish to pass other source of multipart binary data than regular file + */ +@OptIn(InternalSerializationApi::class) +actual suspend fun HttpClient.uniupload( + url: String, + data: Map, + resultDeserializer: DeserializationStrategy, + headers: Headers, + stringFormat: StringFormat +): T? { + val withBinary = data.values.any { it is File || it is UniUploadFileInfo } + + val formData = formData { + data.forEach { (k, v) -> + when (v) { + is File -> append( + k, + v.inputProviderSync(), + Headers.build { + append(HttpHeaders.ContentType, v.mimeType) + append(HttpHeaders.ContentDisposition, "filename=\"${v.name}\"") + } + ) + is UniUploadFileInfo -> append( + k, + InputProvider(block = v.inputAllocator), + Headers.build { + append(HttpHeaders.ContentType, v.mimeType) + append(HttpHeaders.ContentDisposition, "filename=\"${v.fileName.name}\"") + } + ) + else -> append( + k, + stringFormat.encodeToString(v) + ) + } + } + } + + val response = if (withBinary) { + submitFormWithBinaryData( + url, + formData + ) + } else { + submitForm( + url, + Parameters.build { + formData.forEach { + val formItem = (it as PartData.FormItem) + append(it.name!!, it.value) + } + } + ) + } + + return if (response.status == HttpStatusCode.OK) { + stringFormat.decodeFromString(resultDeserializer, response.bodyAsText()) + } else { + null + } +} diff --git a/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/InputProvider.kt b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/InputProvider.kt new file mode 100644 index 00000000000..c03a604c2e1 --- /dev/null +++ b/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/ktor/common/InputProvider.kt @@ -0,0 +1,5 @@ +package dev.inmo.micro_utils.ktor.common + +import io.ktor.utils.io.core.Input + +typealias LambdaInputProvider = () -> Input