Ktor 从入门到放弃(二) 项目结构与文件访问

这篇主要会讲一下 Ktor 的项目结构,以及怎么样更好的使用相应的资源,那么直接进入正题了。

一个正常的 Ktor 项目的结构是这样的:

project
  | -- src
  |     | -- Application.kt
  | -- resources
  |     | -- application.conf
  |     | -- web
  |           | -- index.html
  |     | -- templates
  | -- web
  |     | -- WEB-INF
  |           | -- web.xml
  | -- build.gradle
  | -- gradle.properties
  | -- settings.gradle

gradle 相关的文件自不必说,project 下的文件其含义如下表所示:

文件(夹) 作用
Application.kt 程序 / Router 的入口
application.conf ktor 的配置
index.html 首页,注意 Ktor 的页面必须放在 resources 内
web.xml servlet 配置,固定文件
src/ 源码放在这
resources/ 资源文件放在这
resources/web/ 页面文件,js, css 等放在这
resources/templates/ 模板文件放在这

Ktor 提供了多种文件访问的方式,可以从 resources 里获得文件。在上一篇中,讲述的 Demo 程序,其本质是自己 response 一个文本,这种做法并没有太大的适用场景,更多的情况是直接返回一个写好的页面。这个时候我们可以把代码改成这样:

fun Application.main() {
    routing {
        static {
            defaultResource("index.html", "web")
            resources("web")
        }
    }
}

static { } 用于对静态资源进行配置,比如说在 get("/") 时,返回 index.html 的内容,而此时并不需要明确的写出 get("/"),只需要写 defaultResource() 即可。

如果觉得不直观,也可以写成这样:

fun Application.main() {
    routing {
        get("/") {
            call.respond(call.resolveResource("index.html", "web"))
        }
    }
}

通过以上方法,我们可以访问到任意的文件,抽一个函数出来即可:

@UseExperimental(io.ktor.util.KtorExperimentalAPI::class)
fun ApplicationCall.resolveFile(path: String, resourcePackage: String? = null, classLoader: ClassLoader = application.environment.classLoader): String? {
    val packagePath = (resourcePackage?.replace('.', '/') ?: "").appendPathPart(path)
    val normalizedPath = Paths.get(packagePath).normalizeAndRelativize()
    val normalizedResource = normalizedPath.toString().replace(File.separatorChar, '/')
    for (url in classLoader.getResources(normalizedResource).asSequence()) {
        when (url.protocol) {
            "file" -> {
                val file = File(url.path.decodeURLPart())
                return if (file.isFile) file.readText() else null
            }
            else -> {
            }
        }
    }
    return null
}

这个函数可以到处使用,需要注意的一点是,在 Ktor 里,有非常多的被标为 Experimental 的内容,如果要使用它们,就必须带上 @UseExperimental(io.ktor.util.KtorExperimentalAPI::class) 注解,否则在编译时,这些函数不会被编译到目标文件,从而引发异常。

同时,需要在 gradle 内增加以下内容,以确保 Experimental 内容被成功编译:

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
    kotlinOptions.freeCompilerArgs += ["-Xuse-experimental=kotlin.Experimental"]
}

现在,你可以顺利的访问到 Ktor 项目内的静态文件了,本篇到此结束。


下一篇预告:《Ktor 从入门到放弃(三) 接受请求与页面组装》

推荐阅读更多精彩内容