Ktorで静的なコンテンツを返す

# はじめに
ここでは、Ktorを使って静的な外部コンテンツ(html/css/jsファイル、画像ファイル等)を返す方法を説明します。
昨日、 [Ktorで静的なページを作ってみた話](https://qiita.com/toliner/items/40afbe1dbbf8fde34533)というタイトルで記事を投稿しましたが、あれはページの動的生成のための手順です。
静的なコンテンツを作成するなら、コード内に記述するのではなく、外部のHTML等のファイルを参照できた方が数百倍良いです。
そして、Ktorには当然、静的コンテンツを返す機能もあります。
公式のドキュメントは[こちら](http://ktor.io/features/static-content.html)


今回のbuild.gradleはこちら

```groovy
group 'test'
version '1.0.0'

buildscript {
    ext.kotlin_version = '1.2.10'
    ext.ktor_version = '0.9.0'

    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'

mainClassName = "io.ktor.server.netty.DevelopmentEngine"

repositories {
    mavenCentral()
    jcenter()
    maven { url "http://dl.bintray.com/kotlin/ktor" }
    maven { url "https://dl.bintray.com/kotlin/kotlinx" }
}

dependencies {
    //kotlin
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    //ktor
    compile "io.ktor:ktor-server-core:$ktor_version"
    compile "io.ktor:ktor-server-netty:$ktor_version"
    //log
    compile "ch.qos.logback:logback-classic:1.2.1"
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

kotlin {
    experimental {
        coroutines "enable"
    }
}

run {
    standardInput = System.in
}


```


# 返すファイルを全部個別指定する
通常の処理では、getリクエストに対して何かを返す時、route{}ブロック内でget(path: String){}を用いて処理をしていました。
静的コンテンツを返す場合、get(){}ではなく、static(remotePath: String){}を用いて処理します。
では、サンプルコードを見てみましょう。

```kotlin:Sample.kt
package test

import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.content.default
import io.ktor.content.file
import io.ktor.content.static
import io.ktor.features.CallLogging
import io.ktor.features.DefaultHeaders
import io.ktor.routing.Routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import java.io.File


fun Application.main() {
    //Start Server
    embeddedServer(Netty) {
        install(DefaultHeaders)
        install(CallLogging)
        install(Routing) {
            static("sample") {
                file("style.css", "./static/sample/style.css")
                file("sample.html", File("./static/sample/sample.html"))
                file("sample.txt", "./static/sample/sample.txt")
                default("./static/index.html")
            }
        }
    }.start()
}
``` 

ここで返してるindex.html

```html:index.html
<!DOCTYPE html>
<html>
<head>
    <title>Sample Index Page</title>
    <link rel="stylesheet" type="text/css" href="sample/style.css">
</head>
<body>
<h1>Sample Page</h1>
<p class="sample_p">Links</p>
<a href="sample/sample.html">Sample HTML</a>
<a href="sample/sample.txt">Sample Text</a>
</body>
</html>
```

ディレクトリ構造

```
.
├── build.gradle
├── settings.gradle
├── src
│   ├── main
│   │   ├── kotlin
│   │   │   └── test
│   │   │       └── Sample.kt
│   │   └── resources
│   │       ├── application.conf
│   │       └── logback.xml
├── static
    ├── index.html
    └── sample
        ├── sample.html
        ├── sample.txt
        └── style.css
```

まず、特定のファイルを返す時は、`file(remotePath: String, localPath: String)`を使います。
このファイルの参照urlは、`http://your.domain.com/staticのremotePath/fileのremotePath`
という形になります。
また、特定のファイルへのアクセスではなく、パス(`http://your.domain.com/staticのremotePath`)にアクセスした時の返すファイルを指定したい場合は、`default(localPath: String)`を使います。
~~この時の注意なんですが、default()で指定したファイルから相対パスでのアクセスを行うと、`http://your.domain.com/ファイルの相対パス`にアクセスされます。
つまり、staticのremotePathが無視されてしまうので、注意が必要になります。~~
記事を書く前はこうだったんですが、記事を書きながら検証し直してみると、ちゃんとremotePathを含めた参照になったんですよね・・・
ココらへんの挙動だれか詳しく検証してみてください

## デフォルトのリソースのフォルダを指定する
先程の例の場合、返す静的コンテンツは全てstaticフォルダにまとめられていました。
そして、すべてのリソースパスに、./staticをつけるのは冗長です。
そこで、静的コンテンツのルートパスを指定します。
サンプルコードはこちら

```kotlin:Sample.kt
package test

import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.content.default
import io.ktor.content.file
import io.ktor.content.static
import io.ktor.content.staticRootFolder
import io.ktor.features.CallLogging
import io.ktor.features.DefaultHeaders
import io.ktor.routing.Routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import java.io.File


fun Application.main() {
    //Start Server
    embeddedServer(Netty) {
        install(DefaultHeaders)
        install(CallLogging)
        install(Routing) {
            static("sample") {
                staticRootFolder = File("./static")
                file("style.css", "sample/style.css")
                file("sample.html", File("sample/sample.html"))
                file("sample.txt", "sample/sample.txt")
                default("index.html")
            }
        }
    }.start()
}
```

見ての通り、static{}ブロック内で、staticRootFolderという変数に、静的コンテンツのルートパスを指定しています。
こうすることで、file()やdefault()のlocalPathを、staticRootFolderからの相対パスで記述することができます。

## static/routeの階層構造
static(){}ブロックは、static(){}ブロック内に宣言することができます。
アクセスUrlは、`http://your.domain.com/parentRemotePath/childRemotePath`という風になります。
ちなみに、staticRootFolderは、特に指定しない限り親のstatic(){}ブロックのものが用いられます。
サンプルコードはこちら

```kotlin:Sample.kt
package test

import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.content.default
import io.ktor.content.file
import io.ktor.content.static
import io.ktor.content.staticRootFolder
import io.ktor.features.CallLogging
import io.ktor.features.DefaultHeaders
import io.ktor.routing.Routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import java.io.File


fun Application.main() {
    //Start Server
    embeddedServer(Netty) {
        install(DefaultHeaders)
        install(CallLogging)
        install(Routing) {
            static("sample") {
                staticRootFolder = File("./static")
                file("style.css", "style.css")
                file("sample.html", File("sample.html"))
                file("sample.txt", "sample.txt")
                default("index.html")
                static("sub") {
                    default("index.html")
                }
            }
        }
    }.start()
}
```

ちなみに、`http://your.domain.com/sample/sub`に使いまわしたindex.htmlからの外部参照なんですが、ちゃんと正常に行われるんですよね。
`http://your.domain.com/sample/sample.html`なんかにリンクされてるんです。
もともとWebフロントエンドに疎いのもあって、ここらへんの仕様がよくわかっていません。

# ディレクトリごとコンテンツを指定する
先程の例のように、1つのフォルダに全部のリソースをまとめていたり、それほどでないにしても特定のフォルダにあるリソース全てをアクセス可能にしたい場合、全てのファイルに関して記述するのは非常に面倒です。
そこで、files()を利用して、フォルダを指定することでフォルダ内の全リソースに関して、返すように指定することができます。
サンプルコードはこちら

```kotlin
package test

import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.content.*
import io.ktor.features.CallLogging
import io.ktor.features.DefaultHeaders
import io.ktor.routing.Routing
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import java.io.File


fun Application.main() {
    //Start Server
    embeddedServer(Netty) {
        install(DefaultHeaders)
        install(CallLogging)
        install(Routing) {
            static("sample") {
                staticRootFolder = File("./static")
                files("./")
                default("index.html")
            }
        }
    }.start()
}
```

はい、先程のfile()群が、1つのfiles()にまとまりました。
この場合、staticRootFolderに含まれている全コンテンツに対してgetリクエストからのレスポンスが行われます。
そして、defaultに関しては別枠なので個別指定する必要があります。

# 最後に
Ktorいじるの楽しい！！
みんなもっと使って！！！
そして情報増やして行きましょう！！！