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

178 Views Asked by At

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

2

There are 2 best solutions below

0
On BEST ANSWER

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()

2
On

You can use the ApplicationCall.respondOutputStream method to send the blob bytes. Here is an example:

val blob = ExposedBlob(Random.nextBytes(1024))

embeddedServer(Netty, port = 8080) {
    routing {
        get {
            call.respondOutputStream { blob.inputStream.copyTo(this) }
        }
    }
}.start(wait = true)