temporal result

This commit is contained in:
InsanusMokrassar 2022-01-17 14:45:12 +06:00
parent ef372ab520
commit a651d8560e
15 changed files with 137 additions and 51 deletions

View File

@ -10,7 +10,7 @@ import org.w3c.files.Blob
fun triggerDownloadFile(fullFileInfo: FullFileInfo) { fun triggerDownloadFile(fullFileInfo: FullFileInfo) {
val hiddenElement = document.createElement("a") as HTMLAnchorElement val hiddenElement = document.createElement("a") as HTMLAnchorElement
val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.byteArrayAllocator().toArrayBuffer()))) val url = URL.createObjectURL(Blob(arrayOf(fullFileInfo.inputProvider().toArrayBuffer())))
hiddenElement.href = url hiddenElement.href = url
hiddenElement.target = "_blank" hiddenElement.target = "_blank"
hiddenElement.download = fullFileInfo.name.name hiddenElement.download = fullFileInfo.name.name

View File

@ -4,9 +4,8 @@ import dev.inmo.postssystem.features.files.common.FullFileInfo
import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.mime_types.KnownMimeTypes import dev.inmo.micro_utils.mime_types.KnownMimeTypes
import dev.inmo.micro_utils.mime_types.findBuiltinMimeType import dev.inmo.micro_utils.mime_types.findBuiltinMimeType
import kotlinx.coroutines.CoroutineScope import io.ktor.utils.io.core.ByteReadPacket
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.khronos.webgl.ArrayBuffer import org.khronos.webgl.ArrayBuffer
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
@ -14,23 +13,23 @@ import org.w3c.files.FileReader
import org.w3c.files.get import org.w3c.files.get
fun uploadFileCallbackForHTMLInputChange( fun uploadFileCallbackForHTMLInputChange(
output: MutableStateFlow<FullFileInfo?>, onSet: (FullFileInfo) -> Unit
scope: CoroutineScope
): (Event) -> Unit = { ): (Event) -> Unit = {
(it.target as? HTMLInputElement) ?.apply { (it.target as? HTMLInputElement) ?.apply {
files ?.also { files -> files ?.also { files ->
files[0] ?.also { file -> files[0] ?.also { file ->
scope.launch {
val reader: FileReader = FileReader() val reader: FileReader = FileReader()
reader.onload = { reader.onload = {
val bytes = ((it.target.asDynamic()).result as ArrayBuffer).toByteArray() val bytes = ((it.target.asDynamic()).result as ArrayBuffer).toByteArray()
output.value = FullFileInfo( onSet(
FullFileInfo(
FileName(file.name), FileName(file.name),
findBuiltinMimeType(file.type) ?: KnownMimeTypes.Any, findBuiltinMimeType(file.type) ?: KnownMimeTypes.Any,
bytes.asAllocator ) {
ByteReadPacket(bytes)
}
) )
Unit
} }
reader.readAsArrayBuffer(file) reader.readAsArrayBuffer(file)
@ -38,4 +37,40 @@ fun uploadFileCallbackForHTMLInputChange(
} }
} }
} }
fun fileCallbackForHTMLInputChange(
onSet: (MPPFile) -> Unit
): (Event) -> Unit = {
(it.target as? HTMLInputElement) ?.apply {
files ?.also { files ->
files[0] ?.also { file ->
onSet(file)
} }
}
}
}
fun uploadFileCallbackForHTMLInputChange(
output: MutableState<FullFileInfo?>
): (Event) -> Unit = uploadFileCallbackForHTMLInputChange {
output.value = it
}
fun uploadFileCallbackForHTMLInputChange(
output: MutableStateFlow<FullFileInfo?>
): (Event) -> Unit = uploadFileCallbackForHTMLInputChange {
output.value = it
}
fun fileCallbackForHTMLInputChange(
output: MutableState<MPPFile?>
): (Event) -> Unit = fileCallbackForHTMLInputChange {
output.value = it
}
fun fileCallbackForHTMLInputChange(
output: MutableStateFlow<MPPFile?>
): (Event) -> Unit = fileCallbackForHTMLInputChange {
output.value = it
}

View File

@ -15,6 +15,7 @@ kotlin {
api "io.insert-koin:koin-core:$koin_version" api "io.insert-koin:koin-core:$koin_version"
api "com.benasher44:uuid:$uuid_version" api "com.benasher44:uuid:$uuid_version"
api "com.soywiz.korlibs.klock:klock:$klock_version" api "com.soywiz.korlibs.klock:klock:$klock_version"
api "io.ktor:ktor-http:$ktor_version"
} }
} }
jvmMain { jvmMain {

View File

@ -0,0 +1,30 @@
package dev.inmo.postssystem.features.common.common
import dev.inmo.micro_utils.common.ByteArrayAllocatorSerializer
import io.ktor.utils.io.core.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
typealias SimpleInputProvider = () -> Input
object SimpleInputProviderSerializer : KSerializer<SimpleInputProvider> {
override val descriptor: SerialDescriptor
get() = ByteArrayAllocatorSerializer.descriptor
override fun deserialize(decoder: Decoder): SimpleInputProvider {
val allocator = ByteArrayAllocatorSerializer.deserialize(decoder)
return {
ByteReadPacket(allocator.invoke())
}
}
override fun serialize(encoder: Encoder, value: SimpleInputProvider) {
ByteArrayAllocatorSerializer.serialize(
encoder
) {
value().readBytes()
}
}
}

View File

@ -1,14 +0,0 @@
package dev.inmo.postssystem.features.content.binary.common
import dev.inmo.micro_utils.common.ByteArrayAllocator
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.postssystem.features.content.common.Content
import kotlinx.serialization.Serializable
@Serializable
data class BinaryContent(
val filename: FileName,
val mimeType: MimeType,
val bytesAllocator: ByteArrayAllocator
) : Content

View File

@ -6,6 +6,6 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder
object BinaryContentSerializerModuleConfigurator : ContentSerializersModuleConfigurator.Element { object BinaryContentSerializerModuleConfigurator : ContentSerializersModuleConfigurator.Element {
override fun PolymorphicModuleBuilder<Content>.invoke() { override fun PolymorphicModuleBuilder<Content>.invoke() {
subclass(BinaryContent::class, BinaryContent.serializer()) subclass(DefaultBinaryContent::class, DefaultBinaryContent.serializer())
} }
} }

View File

@ -0,0 +1,14 @@
package dev.inmo.postssystem.features.content.binary.common
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import dev.inmo.postssystem.features.content.common.BinaryContent
import kotlinx.serialization.Serializable
@Serializable
data class DefaultBinaryContent(
override val filename: FileName,
override val mimeType: MimeType,
override val inputProvider: SimpleInputProvider
) : BinaryContent

View File

@ -2,7 +2,7 @@ package dev.inmo.postssystem.features.content.binary.server
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.UpdatedValuePair import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.postssystem.features.content.binary.common.BinaryContent import dev.inmo.postssystem.features.content.binary.common.DefaultBinaryContent
import dev.inmo.postssystem.features.content.common.* import dev.inmo.postssystem.features.content.common.*
import dev.inmo.postssystem.features.content.server.ServerContentStorage import dev.inmo.postssystem.features.content.server.ServerContentStorage
import dev.inmo.postssystem.features.files.common.* import dev.inmo.postssystem.features.files.common.*
@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.map
class BinaryServerContentStorage( class BinaryServerContentStorage(
private val filesStorage: FilesStorage private val filesStorage: FilesStorage
) : ServerContentStorage<BinaryContent> { ) : ServerContentStorage<DefaultBinaryContent> {
private val FileId.asContentId private val FileId.asContentId
get() = ContentId(string) get() = ContentId(string)
private val ContentId.asFileId private val ContentId.asFileId
@ -20,23 +20,23 @@ class BinaryServerContentStorage(
private val FullFileInfoStorageWrapper.asRegisteredContent private val FullFileInfoStorageWrapper.asRegisteredContent
get() = RegisteredContent( get() = RegisteredContent(
id.asContentId, id.asContentId,
BinaryContent( DefaultBinaryContent(
fileInfo.name, fileInfo.name,
fileInfo.mimeType, fileInfo.mimeType,
fileInfo.byteArrayAllocator fileInfo.inputProvider
) )
) )
private val BinaryContent.asFullFileInfo private val DefaultBinaryContent.asFullFileInfo
get() = FullFileInfo( get() = FullFileInfo(
filename, filename,
mimeType, mimeType,
bytesAllocator inputProvider
) )
override val deletedObjectsIdsFlow: Flow<ContentId> = filesStorage.deletedObjectsIdsFlow.map { it.asContentId } override val deletedObjectsIdsFlow: Flow<ContentId> = filesStorage.deletedObjectsIdsFlow.map { it.asContentId }
override val newObjectsFlow: Flow<RegisteredContent> = filesStorage.newObjectsFlow.map { it.asRegisteredContent } override val newObjectsFlow: Flow<RegisteredContent> = filesStorage.newObjectsFlow.map { it.asRegisteredContent }
override val updatedObjectsFlow: Flow<RegisteredContent> = filesStorage.updatedObjectsFlow.map { it.asRegisteredContent } override val updatedObjectsFlow: Flow<RegisteredContent> = filesStorage.updatedObjectsFlow.map { it.asRegisteredContent }
override suspend fun create(values: List<BinaryContent>): List<RegisteredContent> { override suspend fun create(values: List<DefaultBinaryContent>): List<RegisteredContent> {
return filesStorage.create( return filesStorage.create(
values.map { it.asFullFileInfo } values.map { it.asFullFileInfo }
).map { it.asRegisteredContent } ).map { it.asRegisteredContent }
@ -46,14 +46,14 @@ class BinaryServerContentStorage(
filesStorage.deleteById(ids.map { it.asFileId }) filesStorage.deleteById(ids.map { it.asFileId })
} }
override suspend fun update(id: ContentId, value: BinaryContent): RegisteredContent? { override suspend fun update(id: ContentId, value: DefaultBinaryContent): RegisteredContent? {
return filesStorage.update( return filesStorage.update(
id.asFileId, id.asFileId,
value.asFullFileInfo value.asFullFileInfo
) ?.asRegisteredContent ) ?.asRegisteredContent
} }
override suspend fun update(values: List<UpdatedValuePair<ContentId, BinaryContent>>): List<RegisteredContent> { override suspend fun update(values: List<UpdatedValuePair<ContentId, DefaultBinaryContent>>): List<RegisteredContent> {
return filesStorage.update( return filesStorage.update(
values.map { (id, content) -> values.map { (id, content) ->
id.asFileId to content.asFullFileInfo id.asFileId to content.asFullFileInfo

View File

@ -11,6 +11,7 @@ kotlin {
commonMain { commonMain {
dependencies { dependencies {
api project(":postssystem.features.common.common") api project(":postssystem.features.common.common")
api "dev.inmo:micro_utils.mime_types:$microutils_version"
} }
} }
} }

View File

@ -1,5 +1,8 @@
package dev.inmo.postssystem.features.content.common package dev.inmo.postssystem.features.content.common
import dev.inmo.micro_utils.common.FileName
import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlin.jvm.JvmInline import kotlin.jvm.JvmInline
@ -13,7 +16,21 @@ value class ContentId(val string: String)
* @see ContentSerializersModuleConfigurator.Element * @see ContentSerializersModuleConfigurator.Element
* @see ContentSerializersModuleConfigurator * @see ContentSerializersModuleConfigurator
*/ */
interface Content sealed interface Content
/**
* This type of content represents simple content which is easy to serialize/deserialize and to use
*/
interface SimpleContent : Content
/**
* This type represents some binary data which can be sent with multipart and deserialized from it
*/
interface BinaryContent : Content {
val filename: FileName
val mimeType: MimeType
val inputProvider: SimpleInputProvider
}
/** /**
* Content which is already registered in database. Using its [id] you can retrieve all known * Content which is already registered in database. Using its [id] you can retrieve all known

View File

@ -3,6 +3,8 @@ package dev.inmo.postssystem.features.files.common
import dev.inmo.micro_utils.common.* import dev.inmo.micro_utils.common.*
import dev.inmo.micro_utils.mime_types.MimeType import dev.inmo.micro_utils.mime_types.MimeType
import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer import dev.inmo.micro_utils.serialization.typed_serializer.TypedSerializer
import dev.inmo.postssystem.features.common.common.SimpleInputProvider
import dev.inmo.postssystem.features.common.common.SimpleInputProviderSerializer
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -28,8 +30,8 @@ data class MetaFileInfo(override val name: FileName, override val mimeType: Mime
data class FullFileInfo( data class FullFileInfo(
override val name: FileName, override val name: FileName,
override val mimeType: MimeType, override val mimeType: MimeType,
@Serializable(ByteArrayAllocatorSerializer::class) @Serializable(SimpleInputProviderSerializer::class)
val byteArrayAllocator: ByteArrayAllocator val inputProvider: SimpleInputProvider
) : FileInfo ) : FileInfo
fun FullFileInfo.toMetaFileInfo() = MetaFileInfo(name, mimeType) fun FullFileInfo.toMetaFileInfo() = MetaFileInfo(name, mimeType)

View File

@ -3,6 +3,7 @@ package dev.inmo.postssystem.features.files.common
import dev.inmo.postssystem.features.files.common.storage.ReadFilesStorage import dev.inmo.postssystem.features.files.common.storage.ReadFilesStorage
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadKeyValueRepo import dev.inmo.micro_utils.repos.ReadKeyValueRepo
import io.ktor.utils.io.streams.asInput
import java.io.File import java.io.File
class DiskReadFilesStorage( class DiskReadFilesStorage(
@ -38,7 +39,7 @@ class DiskReadFilesStorage(
it.fileInfo.name, it.fileInfo.name,
it.fileInfo.mimeType it.fileInfo.mimeType
) { ) {
id.file.readBytes() id.file.inputStream().asInput()
} }
) )
} }

View File

@ -36,7 +36,7 @@ class WriteDistFilesStorage(
file = newId.file file = newId.file
} while (file.exists()) } while (file.exists())
metasKeyValueRepo.set(newId, it.toMetaFileInfo()) metasKeyValueRepo.set(newId, it.toMetaFileInfo())
file.writeBytes(it.byteArrayAllocator()) file.writeBytes(it.inputProvider())
FullFileInfoStorageWrapper(newId, it) FullFileInfoStorageWrapper(newId, it)
} }
@ -52,7 +52,7 @@ class WriteDistFilesStorage(
override suspend fun update( override suspend fun update(
id: FileId, id: FileId,
value: FullFileInfo value: FullFileInfo
): FullFileInfoStorageWrapper? = id.file.takeIf { it.exists() } ?.writeBytes(value.byteArrayAllocator()) ?.let { ): FullFileInfoStorageWrapper? = id.file.takeIf { it.exists() } ?.writeBytes(value.inputProvider()) ?.let {
val result = FullFileInfoStorageWrapper(id, value.copy()) val result = FullFileInfoStorageWrapper(id, value.copy())
metasKeyValueRepo.set(id, value.toMetaFileInfo()) metasKeyValueRepo.set(id, value.toMetaFileInfo())

View File

@ -1,4 +1,4 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

View File

@ -1,13 +1,12 @@
package dev.inmo.postssystem.targets.telegram.publication.server package dev.inmo.postssystem.targets.telegram.publication.server
import dev.inmo.micro_utils.mime_types.KnownMimeTypes import dev.inmo.micro_utils.mime_types.KnownMimeTypes
import dev.inmo.postssystem.features.content.binary.common.BinaryContent import dev.inmo.postssystem.features.content.binary.common.DefaultBinaryContent
import dev.inmo.postssystem.features.content.text.common.TextContent import dev.inmo.postssystem.features.content.text.common.TextContent
import dev.inmo.postssystem.features.publication.server.PublicationPost import dev.inmo.postssystem.features.publication.server.PublicationPost
import dev.inmo.postssystem.features.publication.server.PublicationTarget import dev.inmo.postssystem.features.publication.server.PublicationTarget
import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.bot.TelegramBot
import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe import dev.inmo.tgbotapi.extensions.utils.shortcuts.executeUnsafe
import dev.inmo.tgbotapi.requests.abstracts.MultipartFile
import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile import dev.inmo.tgbotapi.requests.abstracts.asMultipartFile
import dev.inmo.tgbotapi.requests.send.SendTextMessage import dev.inmo.tgbotapi.requests.send.SendTextMessage
import dev.inmo.tgbotapi.requests.send.media.* import dev.inmo.tgbotapi.requests.send.media.*
@ -23,7 +22,7 @@ class PublicationTargetTelegram(
post.content.mapNotNull { post.content.mapNotNull {
val content = it.content val content = it.content
when (content) { when (content) {
is BinaryContent -> { is DefaultBinaryContent -> {
val storageFile by lazy { val storageFile by lazy {
StorageFile(content.filename.name, content.bytesAllocator()).asMultipartFile() StorageFile(content.filename.name, content.bytesAllocator()).asMultipartFile()
} }