Search code examples
ktorkotlin-exposed

How to serialize / deserialize BLOB images in Ktor with Exposed?


I am new to Ktor and am currently working on a project where I want to use blobs to store user's avatar images. Now there seems to be no decent documentation on how to achieve this.

As there is no default serializer for ExposedBlob I thought I could try to do this with the same approach I use for LocalDate serialization:

@Serializable
data class ExposedUser(
    // other fields
    @Serializable(with = ExposedBlobSerializer::class)
    val avatarImage: ExposedBlob,
    // other fiels
)

This is my ExposedBlobSerializer:

class ExposedBlobSerializer : KSerializer<ExposedBlob> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ExposedBlob", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: ExposedBlob) {
        val bytes = value.bytes
        encoder.encodeString(Base64.getEncoder().encodeToString(bytes))
    }

    override fun deserialize(decoder: Decoder): ExposedBlob {
        val base64String = decoder.decodeString()
        val bytes = Base64.getDecoder().decode(base64String)
        return ExposedBlob(bytes)
    }
}

So far I can store images into the database, but reading them (e.g. via GET-Request) throws this error:

DEBUG ktor.application - Unhandled: GET - /users. Exception class java.io.IOException: mark/reset not supported
java.io.IOException: mark/reset not supported
    at java.base/java.io.InputStream.reset(InputStream.java:733)
    at java.base/java.io.FilterInputStream.reset(FilterInputStream.java:234)
    at org.jetbrains.exposed.sql.statements.api.ExposedBlob.getBytes(ExposedBlob.kt:8)
...

How do I resolve this? Or should I try using a different approach? Or in general: How to serialize / de-serialize Blob Images for network transfer (get / post requests)? I am thankful for every bit of information, especially documentation or tutorials if you guys know some. Thanks <3


Solution

  • For anyone interested in this, the issue was this line in ExposedBlobSerializer:

    val bytes = value.bytes

    which apparently reads a single byte. Use this instead:

    val bytes = value.inputStream.readAllBytes()