mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-11-17 13:53:49 +00:00
complete universal uniupload
This commit is contained in:
parent
a3090cca9d
commit
844a129094
@ -0,0 +1,37 @@
|
|||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlin.jvm.JvmInline
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
@JvmInline
|
||||||
|
value class Progress private constructor(
|
||||||
|
val of1: Double
|
||||||
|
) {
|
||||||
|
val of1Float
|
||||||
|
get() = of1.toFloat()
|
||||||
|
val of100
|
||||||
|
get() = of1 * 100
|
||||||
|
val of100Float
|
||||||
|
get() = of100.toFloat()
|
||||||
|
val of100Int
|
||||||
|
get() = of100.toInt()
|
||||||
|
|
||||||
|
init {
|
||||||
|
require(of1 in rangeOfValues) {
|
||||||
|
"Progress main value should be in $rangeOfValues, but incoming value is $of1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val rangeOfValues = 0.0 .. 1.0
|
||||||
|
|
||||||
|
val START = Progress(rangeOfValues.start)
|
||||||
|
val COMPLETED = Progress(rangeOfValues.endInclusive)
|
||||||
|
|
||||||
|
operator fun invoke(of1: Double) = Progress(of1.coerceIn(rangeOfValues))
|
||||||
|
operator fun invoke(part: Number, total: Number) = Progress(
|
||||||
|
part.toDouble() / total.toDouble()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
@file:Suppress(
|
||||||
|
"RemoveRedundantCallsOfConversionMethods",
|
||||||
|
"RedundantVisibilityModifier",
|
||||||
|
)
|
||||||
|
|
||||||
|
package dev.inmo.micro_utils.common
|
||||||
|
|
||||||
|
import kotlin.Byte
|
||||||
|
import kotlin.Double
|
||||||
|
import kotlin.Float
|
||||||
|
import kotlin.Int
|
||||||
|
import kotlin.Long
|
||||||
|
import kotlin.Short
|
||||||
|
import kotlin.Suppress
|
||||||
|
|
||||||
|
public operator fun Progress.plus(other: Progress): Progress = Progress(of1 + other.of1)
|
||||||
|
|
||||||
|
public operator fun Progress.minus(other: Progress): Progress = Progress(of1 - other.of1)
|
||||||
|
|
||||||
|
public operator fun Progress.plus(i: Byte): Progress = Progress((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.minus(i: Byte): Progress = Progress((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.times(i: Byte): Progress = Progress((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.div(i: Byte): Progress = Progress((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.rem(i: Byte): Progress = Progress((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.plus(i: Short): Progress = Progress((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.minus(i: Short): Progress = Progress((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.times(i: Short): Progress = Progress((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.div(i: Short): Progress = Progress((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.rem(i: Short): Progress = Progress((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.plus(i: Int): Progress = Progress((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.minus(i: Int): Progress = Progress((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.times(i: Int): Progress = Progress((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.div(i: Int): Progress = Progress((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.rem(i: Int): Progress = Progress((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.plus(i: Long): Progress = Progress((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.minus(i: Long): Progress = Progress((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.times(i: Long): Progress = Progress((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.div(i: Long): Progress = Progress((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.rem(i: Long): Progress = Progress((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.plus(i: Float): Progress = Progress((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.minus(i: Float): Progress = Progress((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.times(i: Float): Progress = Progress((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.div(i: Float): Progress = Progress((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.rem(i: Float): Progress = Progress((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.plus(i: Double): Progress = Progress((of1 + i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.minus(i: Double): Progress = Progress((of1 - i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.times(i: Double): Progress = Progress((of1 * i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.div(i: Double): Progress = Progress((of1 / i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.rem(i: Double): Progress = Progress((of1 % i).toDouble())
|
||||||
|
|
||||||
|
public operator fun Progress.compareTo(other: Progress): Int = (of1 - other.of1).toInt()
|
@ -15,5 +15,9 @@ kotlin {
|
|||||||
api libs.ktor.client
|
api libs.ktor.client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
androidMain {
|
||||||
|
dependsOn jvmMain
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
|
typealias OnUploadCallback = (uploaded: Long, count: Long) -> Unit
|
@ -7,5 +7,5 @@ import io.ktor.client.HttpClient
|
|||||||
expect suspend fun HttpClient.tempUpload(
|
expect suspend fun HttpClient.tempUpload(
|
||||||
fullTempUploadDraftPath: String,
|
fullTempUploadDraftPath: String,
|
||||||
file: MPPFile,
|
file: MPPFile,
|
||||||
onUpload: (uploaded: Long, count: Long) -> Unit = { _, _ -> }
|
onUpload: OnUploadCallback = { _, _ -> }
|
||||||
): TemporalFileId
|
): TemporalFileId
|
||||||
|
@ -4,7 +4,6 @@ import dev.inmo.micro_utils.common.FileName
|
|||||||
import dev.inmo.micro_utils.ktor.common.LambdaInputProvider
|
import dev.inmo.micro_utils.ktor.common.LambdaInputProvider
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.http.Headers
|
import io.ktor.http.Headers
|
||||||
import io.ktor.utils.io.core.Input
|
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
import kotlinx.serialization.StringFormat
|
import kotlinx.serialization.StringFormat
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@ -27,5 +26,6 @@ expect suspend fun <T> HttpClient.uniupload(
|
|||||||
data: Map<String, Any>,
|
data: Map<String, Any>,
|
||||||
resultDeserializer: DeserializationStrategy<T>,
|
resultDeserializer: DeserializationStrategy<T>,
|
||||||
headers: Headers = Headers.Empty,
|
headers: Headers = Headers.Empty,
|
||||||
stringFormat: StringFormat = Json.Default
|
stringFormat: StringFormat = Json,
|
||||||
|
onUpload: OnUploadCallback = { _, _ -> }
|
||||||
): T?
|
): T?
|
||||||
|
@ -55,5 +55,5 @@ suspend fun tempUpload(
|
|||||||
actual suspend fun HttpClient.tempUpload(
|
actual suspend fun HttpClient.tempUpload(
|
||||||
fullTempUploadDraftPath: String,
|
fullTempUploadDraftPath: String,
|
||||||
file: MPPFile,
|
file: MPPFile,
|
||||||
onUpload: (uploaded: Long, count: Long) -> Unit
|
onUpload: OnUploadCallback
|
||||||
): TemporalFileId = dev.inmo.micro_utils.ktor.client.tempUpload(fullTempUploadDraftPath, file, onUpload)
|
): TemporalFileId = dev.inmo.micro_utils.ktor.client.tempUpload(fullTempUploadDraftPath, file, onUpload)
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.common.MPPFile
|
||||||
|
import dev.inmo.micro_utils.common.Progress
|
||||||
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.http.Headers
|
||||||
|
import io.ktor.utils.io.core.readBytes
|
||||||
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
|
import kotlinx.coroutines.job
|
||||||
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
import kotlinx.serialization.StringFormat
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import org.khronos.webgl.Int8Array
|
||||||
|
import org.w3c.files.Blob
|
||||||
|
import org.w3c.xhr.FormData
|
||||||
|
import org.w3c.xhr.TEXT
|
||||||
|
import org.w3c.xhr.XMLHttpRequest
|
||||||
|
import org.w3c.xhr.XMLHttpRequestResponseType
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
actual suspend fun <T> HttpClient.uniupload(
|
||||||
|
url: String,
|
||||||
|
data: Map<String, Any>,
|
||||||
|
resultDeserializer: DeserializationStrategy<T>,
|
||||||
|
headers: Headers,
|
||||||
|
stringFormat: StringFormat,
|
||||||
|
onUpload: OnUploadCallback
|
||||||
|
): T? {
|
||||||
|
val formData = FormData()
|
||||||
|
val answer = CompletableDeferred<T?>(currentCoroutineContext().job)
|
||||||
|
|
||||||
|
data.forEach { (k, v) ->
|
||||||
|
when (v) {
|
||||||
|
is MPPFile -> formData.append(
|
||||||
|
k,
|
||||||
|
v
|
||||||
|
)
|
||||||
|
is UniUploadFileInfo -> formData.append(
|
||||||
|
k,
|
||||||
|
Blob(arrayOf(Int8Array(v.inputAllocator().readBytes().toTypedArray()))),
|
||||||
|
v.fileName.name
|
||||||
|
)
|
||||||
|
else -> formData.append(
|
||||||
|
k,
|
||||||
|
stringFormat.encodeToString(v)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = XMLHttpRequest()
|
||||||
|
request.responseType = XMLHttpRequestResponseType.TEXT
|
||||||
|
request.upload.onprogress = {
|
||||||
|
onUpload(it.loaded.toLong(), it.total.toLong())
|
||||||
|
}
|
||||||
|
request.onload = {
|
||||||
|
if (request.status == 200.toShort()) {
|
||||||
|
answer.complete(
|
||||||
|
stringFormat.decodeFromString(resultDeserializer, request.responseText)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
answer.completeExceptionally(Exception("Something went wrong: $it"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
request.onerror = {
|
||||||
|
answer.completeExceptionally(Exception("Something went wrong: $it"))
|
||||||
|
}
|
||||||
|
request.open("POST", url, true)
|
||||||
|
request.send(formData)
|
||||||
|
|
||||||
|
answer.invokeOnCompletion {
|
||||||
|
runCatching {
|
||||||
|
if (request.readyState != XMLHttpRequest.DONE) {
|
||||||
|
request.abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return answer.await()
|
||||||
|
}
|
@ -18,7 +18,7 @@ internal val MPPFile.mimeType: String
|
|||||||
actual suspend fun HttpClient.tempUpload(
|
actual suspend fun HttpClient.tempUpload(
|
||||||
fullTempUploadDraftPath: String,
|
fullTempUploadDraftPath: String,
|
||||||
file: MPPFile,
|
file: MPPFile,
|
||||||
onUpload: (Long, Long) -> Unit
|
onUpload: OnUploadCallback
|
||||||
): TemporalFileId {
|
): TemporalFileId {
|
||||||
val inputProvider = file.inputProvider()
|
val inputProvider = file.inputProvider()
|
||||||
val fileId = submitFormWithBinaryData(
|
val fileId = submitFormWithBinaryData(
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package dev.inmo.micro_utils.ktor.client
|
package dev.inmo.micro_utils.ktor.client
|
||||||
|
|
||||||
import dev.inmo.micro_utils.ktor.common.input
|
import dev.inmo.micro_utils.common.Progress
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.plugins.onUpload
|
||||||
import io.ktor.client.request.forms.InputProvider
|
import io.ktor.client.request.forms.InputProvider
|
||||||
import io.ktor.client.request.forms.formData
|
import io.ktor.client.request.forms.formData
|
||||||
import io.ktor.client.request.forms.submitForm
|
import io.ktor.client.request.forms.submitForm
|
||||||
@ -13,10 +14,8 @@ import io.ktor.http.HttpStatusCode
|
|||||||
import io.ktor.http.Parameters
|
import io.ktor.http.Parameters
|
||||||
import io.ktor.http.content.PartData
|
import io.ktor.http.content.PartData
|
||||||
import kotlinx.serialization.DeserializationStrategy
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
import kotlinx.serialization.InternalSerializationApi
|
|
||||||
import kotlinx.serialization.StringFormat
|
import kotlinx.serialization.StringFormat
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.serializer
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,13 +25,13 @@ import java.io.File
|
|||||||
* [dev.inmo.micro_utils.common.MPPFile] (File from JS or JVM platform). Also you may pass [UniUploadFileInfo] as value
|
* [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
|
* in case you wish to pass other source of multipart binary data than regular file
|
||||||
*/
|
*/
|
||||||
@OptIn(InternalSerializationApi::class)
|
|
||||||
actual suspend fun <T> HttpClient.uniupload(
|
actual suspend fun <T> HttpClient.uniupload(
|
||||||
url: String,
|
url: String,
|
||||||
data: Map<String, Any>,
|
data: Map<String, Any>,
|
||||||
resultDeserializer: DeserializationStrategy<T>,
|
resultDeserializer: DeserializationStrategy<T>,
|
||||||
headers: Headers,
|
headers: Headers,
|
||||||
stringFormat: StringFormat
|
stringFormat: StringFormat,
|
||||||
|
onUpload: OnUploadCallback
|
||||||
): T? {
|
): T? {
|
||||||
val withBinary = data.values.any { it is File || it is UniUploadFileInfo }
|
val withBinary = data.values.any { it is File || it is UniUploadFileInfo }
|
||||||
|
|
||||||
@ -67,7 +66,11 @@ actual suspend fun <T> HttpClient.uniupload(
|
|||||||
submitFormWithBinaryData(
|
submitFormWithBinaryData(
|
||||||
url,
|
url,
|
||||||
formData
|
formData
|
||||||
)
|
) {
|
||||||
|
onUpload { bytesSentTotal, contentLength ->
|
||||||
|
onUpload(bytesSentTotal, contentLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
submitForm(
|
submitForm(
|
||||||
url,
|
url,
|
||||||
@ -77,7 +80,11 @@ actual suspend fun <T> HttpClient.uniupload(
|
|||||||
append(it.name!!, it.value)
|
append(it.name!!, it.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
) {
|
||||||
|
onUpload { bytesSentTotal, contentLength ->
|
||||||
|
onUpload(bytesSentTotal, contentLength)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (response.status == HttpStatusCode.OK) {
|
return if (response.status == HttpStatusCode.OK) {
|
||||||
|
Loading…
Reference in New Issue
Block a user