kotlin - 使用 ktor 检查多部分请求正文中是否存在所有参数
问题描述
我正在尝试使用 ktor 创建一个多部分请求,其代码如下,
import com.firstapp.modal.response.SuccessResponse
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.PartData
import io.ktor.http.content.forEachPart
import io.ktor.http.content.streamProvider
import io.ktor.locations.Location
import io.ktor.locations.post
import io.ktor.request.isMultipart
import io.ktor.request.receive
import io.ktor.request.receiveMultipart
import io.ktor.response.respond
import io.ktor.routing.Route
import io.ktor.util.getOrFail
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield
import java.io.File
import java.io.InputStream
import java.io.OutputStream
import java.lang.IllegalArgumentException
@Location("/uploadVideo/{title}")
class UploadVideo(val title:String)
fun Route.upload(uploadDir: File) {
post<UploadVideo> {
val multipart = call.receiveMultipart()
var videoFile: File? = null
// Processes each part of the multipart input content of the user
multipart.forEachPart { part ->
when (part) {
is PartData.FormItem -> {
if (part.name != "title")
throw IllegalArgumentException("Title parameter not found")
//title = part.value
}
is PartData.FileItem -> {
if (part.name != "file")
throw IllegalArgumentException("file parameter not found")
val ext = File(part.originalFileName).extension
val file = File(uploadDir, "upload-${System.currentTimeMillis()}-${call.parameters.getOrFail("title").hashCode()}.$ext")
part.streamProvider().use { input -> file.outputStream().buffered().use { output -> input.copyToSuspend(output) } }
videoFile = file
}
}
part.dispose()
}
call.respond(
HttpStatusCode.OK,
SuccessResponse(
videoFile!!,
HttpStatusCode.OK.value,
"video file stored"
)
)
}
}
suspend fun InputStream.copyToSuspend(
out: OutputStream,
bufferSize: Int = DEFAULT_BUFFER_SIZE,
yieldSize: Int = 4 * 1024 * 1024,
dispatcher: CoroutineDispatcher = Dispatchers.IO
): Long {
return withContext(dispatcher) {
val buffer = ByteArray(bufferSize)
var bytesCopied = 0L
var bytesAfterYield = 0L
while (true) {
val bytes = read(buffer).takeIf { it >= 0 } ?: break
out.write(buffer, 0, bytes)
if (bytesAfterYield >= yieldSize) {
yield()
bytesAfterYield %= yieldSize
}
bytesCopied += bytes
bytesAfterYield += bytes
}
return@withContext bytesCopied
}
}
上面的代码或rest api工作正常,但问题是,我想检查所有参数是否可用,即我想发送附加参数以及格式如下的文件,
class VideoDetail(val type: String, val userId: String, val userName: String)
我在这里举一个例子,我想要什么
post("/") { request ->
val requestParamenter = call.receive<UserInsert>()
}
在这里,无论参数是什么,我们传递的都会自动转换成 pojo 类,如果我们没有传递它,它会抛出异常,
所以,我想用多部分实现类似的事情。
解决方案
最后,我能够对问题进行排序,下面是代码,
@Location("/uploadVideo/{id}")
class UploadVideo(val id: Int)
fun Route.upload(uploadDir: File) {
post<UploadVideo> {
val multipart = call.receiveMultipart().readAllParts()
val multiMap = multipart.associateBy { it.name }.toMap()
val data = PersonForm(multiMap)
println(data)
val ext = File(data.file.originalFileName).extension
val file = File(uploadDir, "upload-${System.currentTimeMillis()}-${data.file.originalFileName}")
data.file.streamProvider()
.use { input -> file.outputStream().buffered().use { output -> input.copyToSuspend(output) } }
call.respond(
HttpStatusCode.OK,
SuccessResponse(
file,
HttpStatusCode.OK.value,
"video file stored"
)
)
}
}
suspend fun InputStream.copyToSuspend(
out: OutputStream,
bufferSize: Int = DEFAULT_BUFFER_SIZE,
yieldSize: Int = 4 * 1024 * 1024,
dispatcher: CoroutineDispatcher = Dispatchers.IO
): Long {
return withContext(dispatcher) {
val buffer = ByteArray(bufferSize)
var bytesCopied = 0L
var bytesAfterYield = 0L
while (true) {
val bytes = read(buffer).takeIf { it >= 0 } ?: break
out.write(buffer, 0, bytes)
if (bytesAfterYield >= yieldSize) {
yield()
bytesAfterYield %= yieldSize
}
bytesCopied += bytes
bytesAfterYield += bytes
}
return@withContext bytesCopied
}
}
class PersonForm(map: Map<String?, PartData>) {
val file: PartData.FileItem by map
val type: PartData.FormItem by map
val title: PartData.FormItem by map
override fun toString() = "${file.originalFileName}, ${type.value}, ${title.value}"
}
这种方法的唯一问题是使用地图委托,您必须正确地访问地图中是否存在所有参数,即
val data = PersonForm(multiMap)
println(data)
推荐阅读
- node.js - 我的道具被传递给我的组件,但我无法找出正确的路径
- html - 如何在图像上叠加文本和按钮
- javascript - 如何在 DevTools 控制台中以编程方式模拟输入
- azure - Azure 应用服务中的启动脚本,用于从 keyvault 复制文件
- java - 遍历对象映射列表并使用 Java 8 获取项目的长度
- mysql - Flyway 升级与过渡到在线模式迁移等
- windows - 如何在Powershell下查找当前目录下是否存在文件?
- pine-script - 回顾第一个绿色酒吧
- python - 为什么我的并行化功能做的不多?
- c++ - c ++中的唯一/智能指针向量