完善代码

This commit is contained in:
aoi
2026-05-22 01:55:38 +08:00
committed by Kudomaga
parent c7b38ab800
commit 43b401c52b
4 changed files with 54 additions and 44 deletions
@@ -14,6 +14,7 @@ import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
import io.ktor.server.websocket.* import io.ktor.server.websocket.*
import io.ktor.util.pipeline.* import io.ktor.util.pipeline.*
import io.ktor.util.toMap
import io.legado.app.api.ReturnData import io.legado.app.api.ReturnData
import io.legado.app.api.controller.BookController import io.legado.app.api.controller.BookController
import io.legado.app.api.controller.BookSourceController import io.legado.app.api.controller.BookSourceController
@@ -29,7 +30,9 @@ import io.legado.app.web.socket.RssSourceDebugWebSocket
import io.legado.app.web.utils.AssetsWeb import io.legado.app.web.utils.AssetsWeb
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import splitties.init.appCtx
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File
class KtorServer(private val port: Int) { class KtorServer(private val port: Int) {
private var server: ApplicationEngine? = null private var server: ApplicationEngine? = null
@@ -62,26 +65,37 @@ class KtorServer(private val port: Int) {
WebService.serve() WebService.serve()
val multipart = call.receiveMultipart() val multipart = call.receiveMultipart()
var fileName: String? = null var fileName: String? = null
var fileBytes: ByteArray? = null val tempFile = File(appCtx.cacheDir, "upload_${System.currentTimeMillis()}")
try {
multipart.forEachPart { part -> multipart.forEachPart { part ->
when (part) { when (part) {
is PartData.FormItem -> { is PartData.FormItem -> {
if (part.name == "fileName") fileName = part.value if (part.name == "fileName") fileName = part.value
} }
is PartData.FileItem -> { is PartData.FileItem -> {
fileBytes = part.streamProvider().readBytes() part.streamProvider().use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
if (fileName == null) {
fileName = part.originalFileName
}
} }
else -> {} else -> {}
} }
part.dispose() part.dispose()
} }
if (fileName != null && fileBytes != null) { if (fileName != null && tempFile.exists()) {
val returnData = withContext(Dispatchers.IO) { val returnData = withContext(Dispatchers.IO) {
kotlin.runCatching { kotlin.runCatching {
val uri = LocalBook.saveBookFile(fileBytes!!.inputStream(), fileName!!) tempFile.inputStream().use {
val uri = LocalBook.saveBookFile(it, fileName!!)
LocalBook.importFile(uri) LocalBook.importFile(uri)
ReturnData().setData(true) ReturnData().setData(true)
}
}.getOrElse { }.getOrElse {
LogUtils.e(TAG, it.stackTraceStr)
ReturnData().setErrorMsg(it.localizedMessage ?: "Save book error") ReturnData().setErrorMsg(it.localizedMessage ?: "Save book error")
} }
} }
@@ -89,6 +103,9 @@ class KtorServer(private val port: Int) {
} else { } else {
call.respond(HttpStatusCode.BadRequest, "Missing fileName or fileData") call.respond(HttpStatusCode.BadRequest, "Missing fileName or fileData")
} }
} finally {
if (tempFile.exists()) tempFile.delete()
}
} }
post("/saveReadConfig") { handlePost { BookController.saveWebReadConfig(it) } } post("/saveReadConfig") { handlePost { BookController.saveWebReadConfig(it) } }
post("/saveRssSource") { handlePost { RssSourceController.saveSource(it) } } post("/saveRssSource") { handlePost { RssSourceController.saveSource(it) } }
@@ -113,12 +130,14 @@ class KtorServer(private val port: Int) {
get("{...}") { get("{...}") {
WebService.serve() WebService.serve()
var uri = call.request.uri.substringBefore("?") var uri = call.request.path()
if (uri.endsWith("/")) uri += "index.html" if (uri.endsWith("/")) uri += "index.html"
val inputStream = assetsWeb.getInputStream(uri) val inputStream = assetsWeb.getInputStream(uri)
if (inputStream != null) { if (inputStream != null) {
inputStream.use { stream ->
call.respondOutputStream(ContentType.parse(assetsWeb.getMimeType(uri))) { call.respondOutputStream(ContentType.parse(assetsWeb.getMimeType(uri))) {
inputStream.copyTo(this) stream.copyTo(this)
}
} }
} else { } else {
call.respond(HttpStatusCode.NotFound) call.respond(HttpStatusCode.NotFound)
@@ -195,12 +214,6 @@ class KtorServer(private val port: Int) {
} }
} }
private fun Parameters.toMap(): Map<String, List<String>> {
val map = mutableMapOf<String, List<String>>()
this.forEach { s, list -> map[s] = list }
return map
}
companion object { companion object {
private const val TAG = "KtorServer" private const val TAG = "KtorServer"
} }
@@ -13,7 +13,6 @@ import io.legado.app.help.config.AppConfig
import io.legado.app.ui.config.otherConfig.OtherConfig import io.legado.app.ui.config.otherConfig.OtherConfig
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.consumeEach
import org.koin.core.context.GlobalContext import org.koin.core.context.GlobalContext
import splitties.init.appCtx import splitties.init.appCtx
@@ -28,13 +27,13 @@ class BookSearchWebSocket(private val session: DefaultWebSocketServerSession) :
suspend fun handle() { suspend fun handle() {
try { try {
session.incoming.consumeEach { frame -> for (frame in session.incoming) {
if (frame is Frame.Text) { if (frame is Frame.Text) {
val text = frame.readText() val text = frame.readText()
if (!text.isJson()) { if (!text.isJson()) {
session.send("数据必须为Json格式") session.send("数据必须为Json格式")
session.close(CloseReason(CloseReason.Codes.NORMAL, SEARCH_FINISH)) session.close(CloseReason(CloseReason.Codes.NORMAL, SEARCH_FINISH))
return@consumeEach break
} }
val searchMap = GSON.fromJsonObject<Map<String, String>>(text).getOrNull() val searchMap = GSON.fromJsonObject<Map<String, String>>(text).getOrNull()
if (searchMap != null) { if (searchMap != null) {
@@ -42,7 +41,7 @@ class BookSearchWebSocket(private val session: DefaultWebSocketServerSession) :
if (key.isNullOrBlank()) { if (key.isNullOrBlank()) {
session.send(appCtx.getString(R.string.cannot_empty)) session.send(appCtx.getString(R.string.cannot_empty))
session.close(CloseReason(CloseReason.Codes.NORMAL, SEARCH_FINISH)) session.close(CloseReason(CloseReason.Codes.NORMAL, SEARCH_FINISH))
return@consumeEach break
} }
startSearch(key) startSearch(key)
} }
@@ -7,7 +7,6 @@ import io.legado.app.data.appDb
import io.legado.app.model.Debug import io.legado.app.model.Debug
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.consumeEach
import splitties.init.appCtx import splitties.init.appCtx
/** /**
@@ -21,13 +20,13 @@ class BookSourceDebugWebSocket(private val session: DefaultWebSocketServerSessio
suspend fun handle() { suspend fun handle() {
try { try {
session.incoming.consumeEach { frame -> for (frame in session.incoming) {
if (frame is Frame.Text) { if (frame is Frame.Text) {
val text = frame.readText() val text = frame.readText()
if (!text.isJson()) { if (!text.isJson()) {
session.send("数据必须为Json格式") session.send("数据必须为Json格式")
session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束")) session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束"))
return@consumeEach break
} }
val debugBean = GSON.fromJsonObject<Map<String, String>>(text).getOrNull() val debugBean = GSON.fromJsonObject<Map<String, String>>(text).getOrNull()
if (debugBean != null) { if (debugBean != null) {
@@ -36,7 +35,7 @@ class BookSourceDebugWebSocket(private val session: DefaultWebSocketServerSessio
if (tag.isNullOrBlank() || key.isNullOrBlank()) { if (tag.isNullOrBlank() || key.isNullOrBlank()) {
session.send(appCtx.getString(R.string.cannot_empty)) session.send(appCtx.getString(R.string.cannot_empty))
session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束")) session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束"))
return@consumeEach break
} }
appDb.bookSourceDao.getBookSource(tag)?.let { appDb.bookSourceDao.getBookSource(tag)?.let {
Debug.callback = this@BookSourceDebugWebSocket Debug.callback = this@BookSourceDebugWebSocket
@@ -45,7 +44,7 @@ class BookSourceDebugWebSocket(private val session: DefaultWebSocketServerSessio
} else { } else {
session.send("数据必须为Json格式") session.send("数据必须为Json格式")
session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束")) session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束"))
return@consumeEach break
} }
} }
} }
@@ -7,7 +7,6 @@ import io.legado.app.data.appDb
import io.legado.app.model.Debug import io.legado.app.model.Debug
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.consumeEach
import splitties.init.appCtx import splitties.init.appCtx
/** /**
@@ -21,13 +20,13 @@ class RssSourceDebugWebSocket(private val session: DefaultWebSocketServerSession
suspend fun handle() { suspend fun handle() {
try { try {
session.incoming.consumeEach { frame -> for (frame in session.incoming) {
if (frame is Frame.Text) { if (frame is Frame.Text) {
val text = frame.readText() val text = frame.readText()
if (!text.isJson()) { if (!text.isJson()) {
session.send("数据必须为Json格式") session.send("数据必须为Json格式")
session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束")) session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束"))
return@consumeEach break
} }
val debugBean = GSON.fromJsonObject<Map<String, String>>(text).getOrNull() val debugBean = GSON.fromJsonObject<Map<String, String>>(text).getOrNull()
if (debugBean != null) { if (debugBean != null) {
@@ -35,7 +34,7 @@ class RssSourceDebugWebSocket(private val session: DefaultWebSocketServerSession
if (tag.isNullOrBlank()) { if (tag.isNullOrBlank()) {
session.send(appCtx.getString(R.string.cannot_empty)) session.send(appCtx.getString(R.string.cannot_empty))
session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束")) session.close(CloseReason(CloseReason.Codes.NORMAL, "调试结束"))
return@consumeEach break
} }
appDb.rssSourceDao.getByKey(tag)?.let { appDb.rssSourceDao.getByKey(tag)?.let {
Debug.callback = this@RssSourceDebugWebSocket Debug.callback = this@RssSourceDebugWebSocket