【问题标题】:Serving static resources with Ktor GraalVM使用 Ktor GraalVM 提供静态资源
【发布时间】:2022-10-20 23:20:11
【问题描述】:

我找到了 Ktor GraalVM https://ktor.io/docs/graalvm.html 的示例,它运行良好。但是如何提供静态资源,比如图片或者更重要的 js (javascript) 文件呢?

我有:

    routing {
        get("/") {
            call.respondText("Hello GraalVM!")
            call.application.environment.log.info("Call made to /")
        }

        static("/") {
            resources("img")
        }
    }

新代码是 'static("/")...' 部分,又是一个问题:是否可以提供静态内容?当然,我仍然想要原生 GraalVM 输出(可执行文件)。

http://0.0.0.0:8080/ 工作并提供服务:“Hello GraalVM!”输出。 但是http://0.0.0.0:8080/img/test.png(在资源-> img 文件夹中我有一个文件 test.png)返回 404 未找到。

【问题讨论】:

标签: ktor graalvm


【解决方案1】:

我已经让它工作了......并找到了为什么它不能只使用经典代码:

    static("/") {
        staticBasePackage = "static"
        static("img") {
            resources("img")
        }
    }

...但首先是调用的片段,即日志:

>>> url.protocol = resource

这就是答案……它通常作为文件工作,但在本机 GraalVM 中,它被视为资源。 原始代码:

@InternalAPI
public fun resourceClasspathResource(url: URL, path: String, mimeResolve: (String) -> ContentType): OutgoingContent? {
    return when (url.protocol) {
        "file" -> {
            val file = File(url.path.decodeURLPart())
            if (file.isFile) LocalFileContent(file, mimeResolve(file.extension)) else null
        }
        "jar" -> {
            if (path.endsWith("/")) {
                null
            } else {
                val zipFile = findContainingJarFile(url.toString())
                val content = JarFileContent(zipFile, path, mimeResolve(url.path.extension()))
                if (content.isFile) content else null
            }
        }
        "jrt" -> {
            URIFileContent(url, mimeResolve(url.path.extension()))
        }
        else -> null
    }
}

如您所见,这里不支持该资源。我不得不像这样重写代码:

// "new version" of: io.ktor.server.http.content.StaticContentResolutionKt.resourceClasspathResource
    fun resourceClasspathResourceVersion2(url: URL, path: String, mimeResolve: (String) -> ContentType, classLoader: ClassLoader): OutgoingContent? {
        println(">>> url.protocol = ${url.protocol}")
        return when (url.protocol) {
            "file" -> {
                val file = File(url.path.decodeURLPart())
                println(">>> file = $file")
                if (file.isFile) {
                    val localFileContent = LocalFileContent(file, mimeResolve(file.extension))
                    println(">>> localFileContent = $localFileContent")
                    localFileContent
                } else null
            }
            // ... here are other things which are in original version
            "resource" -> {
                println(">>> in resource")
                val resourceName = url.path.substring(1)
                println(">>> resourceName = $resourceName")
                val resourceAsStream = classLoader.getResourceAsStream(resourceName)
                val cnt = ByteArrayContent(resourceAsStream.readAllBytes(), ContentType.parse("image/gif"), HttpStatusCode.OK)
                cnt
            }
            else -> null
        }
    }

路由类 (Routing.kt) 的所有代码如下所示:

package io.ktorgraal.plugins

import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.server.application.*
import io.ktor.server.http.content.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import java.io.File
import java.net.URL

fun Application.configureRouting() {

    // I use println not some Logger to make it simple... in native also
    // (there are different problems in native compilations with loggers)

    // "new version" of: io.ktor.server.http.content.StaticContentResolutionKt.resourceClasspathResource
    fun resourceClasspathResourceVersion2(url: URL, path: String, mimeResolve: (String) -> ContentType, classLoader: ClassLoader): OutgoingContent? {
        println(">>> url.protocol = ${url.protocol}")
        return when (url.protocol) {
            "file" -> {
                val file = File(url.path.decodeURLPart())
                println(">>> file = $file")
                if (file.isFile) {
                    val localFileContent = LocalFileContent(file, mimeResolve(file.extension))
                    println(">>> localFileContent = $localFileContent")
                    localFileContent
                } else null
            }
            // ... here are other things which are in original version
            "resource" -> {
                println(">>> in resource")
                val resourceName = url.path.substring(1)
                println(">>> resourceName = $resourceName")
                val resourceAsStream = classLoader.getResourceAsStream(resourceName)
                val cnt = ByteArrayContent(resourceAsStream.readAllBytes(), ContentType.parse("image/gif"), HttpStatusCode.OK)
                cnt
            }
            else -> null
        }
    }

    // Starting point for a Ktor app:
    routing {
        get("/") {
            call.respondText("Hello GraalVM!")
            call.application.environment.log.info("Call made to /")
        }

        // not working... so we need below "reimplementation"
        /*static("/") {
            staticBasePackage = "static"
            static("img") {
                resources("img")
            }
        }*/

        // "new version" of: io.ktor.server.http.content.StaticContentKt.resources
        // ... because I need to call different function no resourceClasspathResource, but resourceClasspathResourceVersion2
        get("/{pathParameterName...}") {
            println(">>> this endpoint was called")
            val relativePath = call.parameters.getAll("pathParameterName")?.joinToString(File.separator) ?: return@get
            val mimeResolve: (String) -> ContentType = { ContentType.defaultForFileExtension(it) }
            val classLoader: ClassLoader = application.environment.classLoader
            val normalizedPath = relativePath // here I do not do normalization although in Ktor code such code exists
            println(">>> normalizedPath = $normalizedPath")
            val resources = classLoader.getResources(normalizedPath)
            for (url in resources.asSequence()) {
                println(">>> url = $url")
                resourceClasspathResourceVersion2(url, normalizedPath, mimeResolve, classLoader)?.let { content ->
                    if (content != null) {
                        println(">>> about to return content")
                        call.respond(content)
                    }
                }
            }
        }
    }

}

在这里,我也必须拥有 io.ktor.server.http.content.StaticContentKt.resources 的“新版本”,而不是更改整个库。 然后

http://localhost:8080/static/img/test/test.gif

调用工作并返回一个文件。

整个代码更改“https://github.com/ktorio/ktor-samples/tree/main/graalvm”是上面列出的 Routing.kt 类。您还应该将诸如 gif 之类的文件添加到资源 -> 静态 -> img -> 测试 -> test.gif (嵌套仅用于显示结构)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-26
    • 2019-12-13
    • 2016-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多