實作素材上傳
This commit is contained in:
parent
acf72d0aa7
commit
50dbcf60e1
@ -33,4 +33,5 @@ dependencies {
|
|||||||
implementation("org.litote.kmongo:kmongo:$kmongo_version")
|
implementation("org.litote.kmongo:kmongo:$kmongo_version")
|
||||||
implementation("io.ktor:ktor-server-auth-jvm:$ktor_version")
|
implementation("io.ktor:ktor-server-auth-jvm:$ktor_version")
|
||||||
implementation("io.ktor:ktor-server-auth-jwt-jvm:$ktor_version")
|
implementation("io.ktor:ktor-server-auth-jwt-jvm:$ktor_version")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0")
|
||||||
}
|
}
|
||||||
@ -2,15 +2,20 @@ package com.ray650128
|
|||||||
|
|
||||||
import com.ray650128.model.dto.UserDto
|
import com.ray650128.model.dto.UserDto
|
||||||
import com.ray650128.plugins.configureUserRouting
|
import com.ray650128.plugins.configureUserRouting
|
||||||
import com.ray650128.plugins.configureSerialization
|
import com.ray650128.plugins.configureArMaterialRouting
|
||||||
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
import io.ktor.server.auth.*
|
import io.ktor.server.auth.*
|
||||||
import io.ktor.server.auth.jwt.*
|
import io.ktor.server.auth.jwt.*
|
||||||
import io.ktor.server.engine.*
|
import io.ktor.server.engine.*
|
||||||
import io.ktor.server.netty.*
|
import io.ktor.server.netty.*
|
||||||
|
import io.ktor.server.plugins.contentnegotiation.*
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
|
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
|
||||||
|
install(ContentNegotiation) {
|
||||||
|
json()
|
||||||
|
}
|
||||||
install(Authentication) {
|
install(Authentication) {
|
||||||
jwt {
|
jwt {
|
||||||
verifier(JwtConfig.verifier)
|
verifier(JwtConfig.verifier)
|
||||||
@ -26,6 +31,6 @@ fun main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
configureUserRouting()
|
configureUserRouting()
|
||||||
configureSerialization()
|
configureArMaterialRouting()
|
||||||
}.start(wait = true)
|
}.start(wait = true)
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/main/kotlin/com/ray650128/extension/MaterialExtension.kt
Normal file
28
src/main/kotlin/com/ray650128/extension/MaterialExtension.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.ray650128.extension
|
||||||
|
|
||||||
|
import com.ray650128.model.dto.MaterialDto
|
||||||
|
import com.ray650128.model.dto.UserDto
|
||||||
|
import com.ray650128.model.pojo.Material
|
||||||
|
import com.ray650128.model.pojo.User
|
||||||
|
|
||||||
|
fun Material.toDto(): MaterialDto =
|
||||||
|
MaterialDto(
|
||||||
|
id = this.id.toString(),
|
||||||
|
ownerId = this.ownerId.toString(),
|
||||||
|
name = this.name,
|
||||||
|
path = this.path,
|
||||||
|
contentType = this.contentType,
|
||||||
|
fileTag = this.fileTag,
|
||||||
|
createAt = this.createAt,
|
||||||
|
updatedAt = this.updatedAt
|
||||||
|
)
|
||||||
|
|
||||||
|
fun MaterialDto.toMaterial(): Material =
|
||||||
|
Material(
|
||||||
|
name = this.name,
|
||||||
|
path = this.path,
|
||||||
|
contentType = this.contentType,
|
||||||
|
fileTag = this.fileTag,
|
||||||
|
createAt = this.createAt,
|
||||||
|
updatedAt = this.updatedAt
|
||||||
|
)
|
||||||
@ -7,5 +7,6 @@ data class ErrorResponse(val message: String) {
|
|||||||
companion object {
|
companion object {
|
||||||
val NOT_FOUND_RESPONSE = ErrorResponse("Person was not found")
|
val NOT_FOUND_RESPONSE = ErrorResponse("Person was not found")
|
||||||
val BAD_REQUEST_RESPONSE = ErrorResponse("Invalid request")
|
val BAD_REQUEST_RESPONSE = ErrorResponse("Invalid request")
|
||||||
|
val UNAUTHORIZED_RESPONSE = ErrorResponse("Unauthorized")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
16
src/main/kotlin/com/ray650128/model/dto/MaterialDto.kt
Normal file
16
src/main/kotlin/com/ray650128/model/dto/MaterialDto.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.ray650128.model.dto
|
||||||
|
|
||||||
|
import io.ktor.server.auth.*
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class MaterialDto(
|
||||||
|
val id: String? = null,
|
||||||
|
var ownerId: String? = null,
|
||||||
|
var name: String,
|
||||||
|
val path: String,
|
||||||
|
val contentType: String,
|
||||||
|
var fileTag: ArrayList<String>? = null,
|
||||||
|
var createAt: Long? = null,
|
||||||
|
var updatedAt: Long? = null
|
||||||
|
): Principal
|
||||||
15
src/main/kotlin/com/ray650128/model/pojo/Material.kt
Normal file
15
src/main/kotlin/com/ray650128/model/pojo/Material.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.ray650128.model.pojo
|
||||||
|
|
||||||
|
import org.bson.codecs.pojo.annotations.BsonId
|
||||||
|
import org.litote.kmongo.Id
|
||||||
|
|
||||||
|
data class Material(
|
||||||
|
@BsonId var id: Id<Material>? = null,
|
||||||
|
var ownerId: Id<User>? = null,
|
||||||
|
var name: String,
|
||||||
|
var path: String,
|
||||||
|
var contentType: String,
|
||||||
|
var fileTag: ArrayList<String>? = null,
|
||||||
|
var createAt: Long? = null,
|
||||||
|
var updatedAt: Long? = null
|
||||||
|
)
|
||||||
8
src/main/kotlin/com/ray650128/model/pojo/NewMaterial.kt
Normal file
8
src/main/kotlin/com/ray650128/model/pojo/NewMaterial.kt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package com.ray650128.model.pojo
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class NewMaterial(
|
||||||
|
var name: String
|
||||||
|
)
|
||||||
@ -1,16 +1,128 @@
|
|||||||
package com.ray650128.plugins
|
package com.ray650128.plugins
|
||||||
|
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
import com.ray650128.extension.*
|
||||||
import io.ktor.server.plugins.contentnegotiation.*
|
import com.ray650128.model.ErrorResponse
|
||||||
import io.ktor.server.response.*
|
import com.ray650128.model.dto.MaterialDto
|
||||||
|
import com.ray650128.model.dto.UserDto
|
||||||
|
import com.ray650128.model.pojo.Material
|
||||||
|
import com.ray650128.model.pojo.NewMaterial
|
||||||
|
import com.ray650128.service.MaterialService
|
||||||
|
import com.ray650128.service.UserService
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.http.content.*
|
||||||
import io.ktor.server.application.*
|
import io.ktor.server.application.*
|
||||||
|
import io.ktor.server.auth.*
|
||||||
|
import io.ktor.server.request.*
|
||||||
|
import io.ktor.server.response.*
|
||||||
import io.ktor.server.routing.*
|
import io.ktor.server.routing.*
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.litote.kmongo.Id
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun Application.configureArMaterialRouting() {
|
||||||
|
|
||||||
|
val userService = UserService()
|
||||||
|
val materialService = MaterialService()
|
||||||
|
|
||||||
fun Application.configureSerialization() {
|
|
||||||
install(ContentNegotiation) {
|
|
||||||
json()
|
|
||||||
}
|
|
||||||
routing {
|
routing {
|
||||||
|
route("/upload") {
|
||||||
|
get("/{name}") {
|
||||||
|
val fileName = call.parameters["name"] ?: run {
|
||||||
|
call.sendNotFound(ErrorResponse.NOT_FOUND_RESPONSE)
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
println("[File]filename: $fileName")
|
||||||
|
val file = File("./upload/$fileName")
|
||||||
|
println("[File]file: ${file.absolutePath}")
|
||||||
|
if(!file.exists()) {
|
||||||
|
call.sendNotFound(ErrorResponse.NOT_FOUND_RESPONSE)
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
call.respondFile(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticate {
|
||||||
|
route("/api") {
|
||||||
|
route("/v1") {
|
||||||
|
route("/materials") {
|
||||||
|
post("/upload") {
|
||||||
|
val account = call.authentication.principal<UserDto>()?.account ?: run {
|
||||||
|
call.sendUnauthorized(ErrorResponse.UNAUTHORIZED_RESPONSE)
|
||||||
|
return@post
|
||||||
|
}
|
||||||
|
val user = userService.findByAccount(account) ?: run {
|
||||||
|
call.sendUnauthorized(ErrorResponse.UNAUTHORIZED_RESPONSE)
|
||||||
|
return@post
|
||||||
|
}
|
||||||
|
var materialId: String? = null
|
||||||
|
// retrieve all multipart data (suspending)
|
||||||
|
val multipart = call.receiveMultipart()
|
||||||
|
var params: NewMaterial? = null
|
||||||
|
multipart.forEachPart { part ->
|
||||||
|
// Check part type
|
||||||
|
when (part) {
|
||||||
|
is PartData.FormItem -> {
|
||||||
|
params = Json.decodeFromString(NewMaterial.serializer(), part.value)
|
||||||
|
println(part.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
is PartData.FileItem -> {
|
||||||
|
// retrieve file name of upload
|
||||||
|
println("File content type: ${part.contentType?.contentType}/${part.contentType?.contentSubtype}")
|
||||||
|
val extensionName = when (part.contentType?.contentSubtype) {
|
||||||
|
"gltf-binary" -> "glb"
|
||||||
|
"gltf+json" -> "gltf"
|
||||||
|
else -> part.contentType?.contentSubtype
|
||||||
|
}
|
||||||
|
val name = "${System.currentTimeMillis()}.$extensionName"
|
||||||
|
val directory = File("./upload/")
|
||||||
|
if (!directory.exists()) {
|
||||||
|
directory.mkdir()
|
||||||
|
}
|
||||||
|
val file = File("${directory.path}/$name")
|
||||||
|
|
||||||
|
// use InputStream from part to save file
|
||||||
|
part.streamProvider().use { its ->
|
||||||
|
// copy the stream to the file with buffering
|
||||||
|
file.outputStream().buffered().use {
|
||||||
|
// note that this is blocking
|
||||||
|
its.copyTo(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val tmpMaterial = MaterialDto(
|
||||||
|
name = if (params == null) {
|
||||||
|
part.originalFileName!!
|
||||||
|
} else {
|
||||||
|
params!!.name
|
||||||
|
},
|
||||||
|
path = "/upload/$name",
|
||||||
|
contentType = "${part.contentType}",
|
||||||
|
createAt = System.currentTimeMillis()
|
||||||
|
).toMaterial().apply {
|
||||||
|
ownerId = user.id!!
|
||||||
|
}
|
||||||
|
materialId = materialService.create(tmpMaterial).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
// make sure to dispose of the part after use to prevent leaks
|
||||||
|
part.dispose()
|
||||||
|
}
|
||||||
|
if (materialId != null) {
|
||||||
|
val data = materialService.findById(materialId!!)?.toDto()
|
||||||
|
call.sendSuccess(data)
|
||||||
|
} else {
|
||||||
|
call.sendBadRequest(ErrorResponse("Add material fail."))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get("/json/kotlinx-serialization") {
|
get("/json/kotlinx-serialization") {
|
||||||
call.respond(mapOf("hello" to "world"))
|
call.respond(mapOf("hello" to "world"))
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/main/kotlin/com/ray650128/service/MaterialService.kt
Normal file
45
src/main/kotlin/com/ray650128/service/MaterialService.kt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package com.ray650128.service
|
||||||
|
|
||||||
|
import com.ray650128.model.pojo.Material
|
||||||
|
import org.bson.types.ObjectId
|
||||||
|
import org.litote.kmongo.*
|
||||||
|
import org.litote.kmongo.id.toId
|
||||||
|
|
||||||
|
class MaterialService {
|
||||||
|
private val client = KMongo.createClient("mongodb://www.ray650128.com:27017")
|
||||||
|
private val database = client.getDatabase("ar_system")
|
||||||
|
private val userCollection = database.getCollection<Material>()
|
||||||
|
|
||||||
|
fun create(user: Material): Id<Material>? {
|
||||||
|
userCollection.insertOne(user)
|
||||||
|
return user.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findAll(): List<Material> = userCollection.find().toList()
|
||||||
|
|
||||||
|
fun findById(id: String): Material? {
|
||||||
|
val bsonId: Id<Material> = ObjectId(id).toId()
|
||||||
|
return userCollection.findOne(Material::id eq bsonId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findByName(name: String): List<Material> {
|
||||||
|
val caseSensitiveTypeSafeFilter = Material::name regex name
|
||||||
|
return userCollection.find(caseSensitiveTypeSafeFilter).toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateById(id: String, request: Material): Boolean =
|
||||||
|
findById(id)?.let { user ->
|
||||||
|
val updateResult = userCollection.replaceOne(
|
||||||
|
user.copy(
|
||||||
|
name = request.name,
|
||||||
|
updatedAt = request.updatedAt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
updateResult.modifiedCount == 1L
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
fun deleteById(id: String): Boolean {
|
||||||
|
val deleteResult = userCollection.deleteOneById(ObjectId(id))
|
||||||
|
return deleteResult.deletedCount == 1L
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user