Как прочитать текстовый файл из ресурсов в Котлине?

97

Я хочу написать тест Spek на Котлине. Тест должен прочитать HTML-файл из src/test/resourcesпапки. Как это сделать?

class MySpec : Spek({

    describe("blah blah") {

        given("blah blah") {

            var fileContent : String = ""

            beforeEachTest {
                // How to read the file file.html in src/test/resources/html
                fileContent = ...  
            }

            it("should blah blah") {
                ...
            }
        }
    }
})
Олаф
источник

Ответы:

116
val fileContent = MySpec::class.java.getResource("/html/file.html").readText()
JB Nizet
источник
30
У меня это не сработало, пришлось поменять наthis::class.java.classLoader.getResource("/html/file.html").readText()
pavlos163
4
Для меня оба эти варианта работали в приложении для Android (обратите внимание на лишнее /в одном из них, которое нужно удалить в другом): this::class.java.getResource("/html/file.html").readText()иthis::class.java.classLoader.getResource("html/file.html").‌​readText()
Франко
20
val fileContent = javaClass.getResource("/html/file.html").readText()делает работу еще короче
Фрэнк Неблунг
1
Как ни странно, мне всегда нужна косая черта. Например, если файл находится в корневом каталоге ресурсов, вы все равно должны ссылаться на него как на «/file.html»
Сомайя Кумбера
28

еще одно немного другое решение:

@Test
fun basicTest() {
    "/html/file.html".asResource {
        // test on `it` here...
        println(it)
    }

}

fun String.asResource(work: (String) -> Unit) {
    val content = this.javaClass::class.java.getResource(this).readText()
    work(content)
}
Jhodges
источник
Хорошее использование функции расширения! Но почему вы здесь используете лямбда-функцию? Для меня это не имеет особого смысла. Более того, эта thisдеталь у меня не подошла. Таким образом, я рекомендую следующее:fun String.asResource(): URL? = object {}.javaClass.getResource(this)
Qw3ry
Мне нравится этот метод работы, когда вы объявляете файл, а затем работаете над его содержимым. Думаю, личные предпочтения. thisв приведенном выше примере относится к строковому объекту.
jhodges
это ужасное злоупотребление функцией расширения. Загрузка файлов не относится к классу String.
Бен
именно в этом контексте. Я бы не стал делать это глобально доступным или использовать вне тестовых классов. Я считаю, что здесь это скорее функция отображения.
jhodges
28

Понятия не имею, почему это так сложно, но самый простой способ, который я нашел (без ссылки на конкретный класс):

fun getResourceAsText(path: String): String {
    return object {}.javaClass.getResource(path).readText()
}

А затем передать абсолютный URL-адрес, например

val html = getResourceAsText("/www/index.html")
Рассел Бриггс
источник
это {}требуется? Почему не просто так javaClass.getResource(path).readText()?
andrewgazelka
2
javaClass должен вызываться для объекта в соответствии с документами kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html Если он работает без него, тогда выбирайте жизнь :)
Рассел Бриггс,
3
Одним из недостатков описанного выше метода является создание нового объекта при каждом доступе к ресурсу. Было бы лучше сохранить фиктивный объект вне функции.
Рассел Бриггс
@RussellBriggs tbh Я не думаю, что это имеет большое значение. Производительность создания объекта не является проблемой, если вы обращаетесь к диску!
Qw3ry
13

Немного другое решение:

class MySpec : Spek({
    describe("blah blah") {
        given("blah blah") {

            var fileContent = ""

            beforeEachTest {
                html = this.javaClass.getResource("/html/file.html").readText()
            }

            it("should blah blah") {
                ...
            }
        }
    }
})
Олаф
источник
По какой-то причине у меня это не сработало. Работал только явный вызов класса. Просто добавляю для других. Я думаю, это как-то связано с tornadofx
nmu
После создания тестового входного файла в /src/test/resources, this.javaClass.getResource("/<test input filename>")работал, как ожидалось. Спасибо за решение выше.
jkwuc89
что делать, если fileContent не String, и я не буду создавать фиктивный объект?
minizibi 08
1
ведущая косая черта перед путем кажется здесь обязательной, тогда как в Java я обычно ее опускаю.
cakraww 07
6

Котлин + Весенний способ:

@Autowired
private lateinit var resourceLoader: ResourceLoader

fun load() {
    val html = resourceLoader.getResource("classpath:html/file.html").file
        .readText(charset = Charsets.UTF_8)
}
naXa
источник
5
val fileContent = javaClass.getResource("/html/file.html").readText()
Дживимберг
источник
5
private fun loadResource(file: String) = {}::class.java.getResource(file).readText()
Олаф
источник
4

Использование класса ресурсов библиотеки Google Guava :

import com.google.common.io.Resources;

val fileContent: String = Resources.getResource("/html/file.html").readText()
Lu55
источник
хорошо, что Guave сообщает имя файла, если ресурс не найден - гораздо лучше для устранения неполадок
Nishi
2

Я предпочитаю это делать так:

fun getResourceText(path: String): String {
    return File(ClassLoader.getSystemResource(path).file).readText()
}
Саидспен
источник
0

Вам может пригодиться класс File:

import java.io.File

fun main(args: Array<String>) {
  val content = File("src/main/resources/input.txt").readText()
  print(content)
} 
Кшиштоф Зиомек
источник
3
Этот ответ вводит в заблуждение. Это не загружает «ресурс», а загружает файл прямо из файловой системы, а не по пути к классам. Он больше не будет работать после сборки приложения, так как вы попытаетесь ссылаться на несуществующие файлы, вместо того, чтобы загружать их из файла jar.
Бен
@ben Спасибо за ваш комментарий. Вопрос касался чтения файла с ресурса в тесте kotlin Spek.
Кшиштоф Зиомек,