やりたかったこと
サーバサイドkotlinでKtorを使ってmultipart/form-data形式のファイルを取得したい。
画像でもtxtでもjsonでもdatでも。
実装
※動作確認したいだけならResourceとServiceに分ける必要はないです
fun.Route.hoge(hogeService: HogeService) {
route("hoge") {
post("/") {
val multiPartData = call.receiveMultiPart()
val res = hogeService.addHoge(multiPartData)
call.respond(HttpStatusCode.OK, res)
}
}
}
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にファイル以外の別のパラメータも含んでいる場合はこんな感じ。
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