Serving Static Content
Whether we’re creating a website or an HTTP endpoint, many applications need to serve files (such as stylesheets, scripts, images, etc.), While it is certainly possible with Ktor to load the contents of a file and send it in response to a request, given this is such a common functionality, Ktor simplifies the entire process for us with the static Feature.
The first step in defining a static route is to define the path under which the content should be served. For instance, if you want everything under the route assets to be treated as static content, you need to add the following to your application setup:
1. routing {
2. static("assets") {
4. }
5. }
The next step is to define where we want the content to be served from, which can be either
Folders
In order to serve the contents from a folder, we need to specify the folder name using the files function. The path is always relative to the application path:
1. routing {
2. static("assets") {
3. files("css")
4. }
5. }
files("css") would then allow for any file located in the folder css to be served as static content under the given URL pattern, which in this case is assets. This means that a request to
/assets/stylesheet.css would serve the file /css/stylesheet.css
We can have as many folders as we like under a single path. For instance the following would also be valid:
1. routing {
2. static("assets") {
3. files("css")
4. files("js")
5. }
6. }
Serving individual files
In addition to serving files from folders, we can also specify individuals files we would like to make available by using the file function. Optionally this takes a second argument which allows us to map a physical filename to a virtual one:
1. routing {
2. static("static") {
3. file("image.png")
4. file("random.txt", "image.png")
5. }
6. }
Defining a default file
For a specific path, we can also define the default file to be loaded:
1. routing {
2. static("assets") {
3. files("css")
4. default("index.html")
5. }
6. }
which would cause a request to /assets/ to serve index.html.
Changing the default root folder
Ktor also provides us the ability to specify a different root folder from where contents is served. This is useful for instance if we want to dynamically define where contents should be served from, or even use absolute paths.
We can do this by setting the value of the staticRootFolder property:
1. static("docs") {
2. staticRootFolder = File("/system/folder/docs")
3. files("public")
4. }
which would then map any request to /docs to the physical folder /system/folder/docs/public.
Embedded Application Resources
We can embed content as resources in our applications and serve these using the resource and resources functions:
1. static("assets") {
2. resources("css")
3. }
resources("css") would then allow for any file located under the resource css to be served as static content under the given URL pattern, which in this case is assets. This means that a request to
/assets/stylesheet.cs would serve the file /css/stylesheet.cs
We can have as many resources as we like under a single path. For instance the following would also be valid:
1. routing {
2. static("assets") {
3. resources("css")
4. resources("js")
5. }
6. }
Serving individual resources
In addition to serving files from resources, we can also specify individuals files we would like to make available by using the resource function. Optionally this takes a second argument which allows us to map a physical filename to a virtual one:
1. routing {
2. static("static") {
3. resource("image.png")
4. resource("random.txt", "image.png")
5. }
6. }
Defining a default resource
For a specific path, we can also define the default file to be loaded:
1. routing {
2. static("assets") {
3. resources("css")
4. defaultResource("index.html")
5. }
6. }
Changing the default resource package
Ktor also provides us the ability to specify a different base resource package from where contents is served.
We can do this by setting the value of the staticBasePackage property:
1. static("docs") {
2. staticBasePackage = File("/system/folder/docs")
3. files("public")
4. }
Sub-routes
If we want to have sub-routes, we can nest static functions:
1. static("assets") {
2. files("css")
3. static("themes") {
4. files("data")
5. }
6. }
allowing for /assets/themes to load files from the /data
Handling errors
If the request content is not found, Ktor will automatically respond with a 404 Not Found HTTP status code. For more information about personalising error handling, please see status pages
Customising Content Type header
Ktor automatically looks up the content type of a file based on its extension and sets the appropriate Content-Type header. The list of supported MIME types is defined in the mimelist.csv resource file located in ktor-server-core artifact.
Example
An example application that serves static files using both folders and resources can be found below:
1. package io.ktor.snippets.staticcontent
3. import io.ktor.application.*
4. import io.ktor.features.*
5. import io.ktor.html.*
6. import io.ktor.http.content.*
7. import io.ktor.routing.*
8. import kotlinx.html.*
9. import java.io.*
11. fun Application.main() {
12. install(DefaultHeaders)
13. install(CallLogging)
14. routing {
15. routeFilesystem()
16. routeResources()
17. }
18. }
20. fun Route.routeFilesystem() {
21. get("/") {
22. call.respondHtml {
23. head {
24. title { +"Ktor: static-content" }
25. styleLink("/static/styles.css")
26. script(src = "/static/script.js") {}
27. }
28. body {
29. p {
30. +"Hello from Ktor static content sample application"
31. }
32. p {
33. +"Current directory is ${System.getProperty("user.dir")}"
34. }
35. img(src = "static/image.png") {
36. onClick = "message('clicked the image')"
37. }
38. }
39. }
40. }
41. static("static") {
42. // When running under IDEA make sure that working directory is set to this sample's project folder
43. staticRootFolder = File("files")
44. files("css")
45. files("js")
46. file("image.png")
47. file("random.txt", "image.png")
48. default("index.html")
49. }
50. static("custom") {
51. staticRootFolder = File("/tmp") // Establishes a root folder
52. files("public") // For this to work, make sure you have /tmp/public on your system
53. static("themes") {
54. // services /custom/themes
55. files("data")
56. }
57. }
58. }
60. fun Route.routeResources() {
61. get("/resources") {
62. call.respondHtml {
63. head {
64. title { +"Ktor: static-content" }
65. styleLink("/static-resources/styles.css")
66. }
67. body {
68. p {
69. +"Hello from Ktor static content served from resources, if the background is cornflowerblue"
70. }
71. }
72. }
73. }
75. static("static-resources") {
76. resources("css")
77. }
78. }
