[Ktor]multipart/form-dataでファイルを取得


やりたかったこと

サーバサイドkotlinでKtorを使ってmultipart/form-data形式のファイルを取得したい。
画像でもtxtでもjsonでもdatでも。

実装

※動作確認したいだけならResourceとServiceに分ける必要はないです

hogeResource.kt
fun.Route.hoge(hogeService: HogeService) {
   route("hoge") {
       post("/") {
           val multiPartData = call.receiveMultiPart() 
           val res = hogeService.addHoge(multiPartData)
           call.respond(HttpStatusCode.OK, res)
       }
   }
}
hogeService.kt
    suspend fun addHoge(multipartData: MultiPartData) : Hoge {
        val file = makeFile(multipartData) ?: throw Exception()

        // この辺は今回関係ないので中略。
        // ファイルからデータを取得してExposedを使いDBに登録してます。

        return getHoge(hoge.id)
    }

    private fun makeFile(multipartData: MultiPartData) : File? {
        // ファイル生成(別にTempFileである必要はないので好みで)
        val file = File.createTempFile("tmp_", ".json")
        val part = multipartData.readPart() ?: return null
        if (part !is PartData.FileItem) return null 
        // ファイル内容の書き込み
        part.streamProvider().use { inputStream ->
            file.outputStream().buffered().use {
                inputStream.copyTo(it)
            }
        }
        return file
   }

・ファイル名
元ファイルから変えないならPartData.FileItem.originalFileNameを使えばよいと思います。

・FileItemの取得
ktorの公式サンプルだとmultipart.forEachPartを使っていますが
ファイルしか受信しないのであればmultipartData.readPart()で短く書けます。
(但し、FileItemだという型チェックはしてあげないといけません)

multipart/form-dataにファイル以外の別のパラメータも含んでいる場合はこんな感じ。

hogeService.kt
var hogeID = 0
var hogeName = ""
multiPartData.forEachPart { part ->
    when (part) {
        is PartData.FormItem -> {
           when (it.name) {
               "hogeID"   -> hogeID   = it.value.toInt()
               "hogeName" -> hogeName = it.value
           }
        }
        is PartData.FileItem -> {
           //(略)
        }
    }
}

いちいちJsonファイルをパースしてDBに登録、なんてことは滅多にやることではないので、
画像か、datで受け取って復号化みたいな用途が本来の使い方ですかね。
あとは、AmazonS3に突っ込むとか。

参考

Client Multipart
https://ktor.io/samples/other/client-multipart.html

PartData
https://api.ktor.io/1.3.0-rc2/io.ktor.http.content/-part-data/index.html