Ответ 1
Предыдущий ответчик (skuro) указал, что мне нужно получить файл из пути к классам. После немного большего копания это, кажется, решение, которое работает для моего случая:
(.getFile (clojure.java.io/resource "foo.png"))
Я использую Leiningen в моем проекте Clojure (приложение GUI) и создал каталог ресурсов в корне проекта для хранения изображений, которые использует мое приложение.
Когда я запускаю свое приложение локально во время тестирования, я получаю изображения с использованием относительного пути "resources/logo.png", и это отлично работает. Но когда я создаю uberjar с помощью Leiningen, Leiningen помещает файлы из папки ресурсов в корневую папку JAR, поэтому мои ссылки на файлы ресурсов больше не работают.
Каков правильный способ доступа к таким ресурсам, как Leiningen?
Предыдущий ответчик (skuro) указал, что мне нужно получить файл из пути к классам. После немного большего копания это, кажется, решение, которое работает для моего случая:
(.getFile (clojure.java.io/resource "foo.png"))
Просто синтаксический сахар для ответа Кевина Альбрехта:
(require '[clojure.java.io :as io])
(-> "foo.png" io/resource io/file)
Leiningen заимствует соглашение о ресурсах от maven, имея несколько разные макеты папок. В правиле указано, что папка resources
должна использоваться как корневой путь класса времени компиляции, что означает, что leiningen прав при размещении всех файлов внутри папки resources
в корневом расположении внутри банки.
Проблема заключается в том, что физическое местоположение!= расположение пути к траектории, так что, если первое изменяется при упаковке приложения, последнее остается неизменным (classpath:/
)
Лучше полагаться на расположение пути к классам, чтобы найти ресурсы файловой системы или вытащить их из банки и поместить их в предсказуемую папку, статическую или настраиваемую.
Это напрямую не отвечает на вопрос OP, но skuro упомянул что-то в своем последнем абзаце, что может быть весьма полезным, т.е. сделать доступными ресурсы на пути к классам во время разработки, но не включать их в выпускную банку /uberjar.
Причиной этого является то, что вы можете размещать ресурсы отдельно за пределами jar – например, размещение файлов конфигурации под /etc
– а затем ссылаться на них во время выполнения, перечисляя такие папки ресурсов в пути к классам.
:resource-paths
из вашего проекта .clj;:profiles {:dev {:resource-paths ["src/main/resources"]}}
Таким образом, ресурсы будут доступны на пути к классам во время разработки (компиляция, тестирование, замена), но не будут включены в выпускные банки.
Как altenative, вы можете захотеть связать некоторые ресурсы с банком релиза, такие как значки приложений и графики, но не другие, такие как файлы конфигурации. В этом случае вы можете разделить папку ресурсов на подкаталоги и объявить те, которые хотите получить на верхнем уровне, а остальные - в профиле dev
:
:resource-paths ["src/main/resources/icons"
"src/main/resources/images"]
:profiles {:dev {:resource-paths ["src/main/resources/config"]}}
Используя вышеописанный метод, вы можете ссылаться на ресурсы как во время разработки, так и производства равномерно, просматривая их по пути к классам (как показано Кевином и Valerii), а не непосредственно в файловой системе:
(require '[clojure.java.io :as jio])
(jio/file (jio/resource "relative/path/to/resource.xml"))
В процессе разработки Leiningen будет включать как ресурсы верхнего уровня, так и dev
в пути к классам. Для выпущенной среды выполнения вам придется самостоятельно настроить путь к классу JRE:
java -cp "/etc/myapp:/usr/local/lib/myapp.jar" myapp.core $*
Кроме того, вы также можете оставить все ресурсы, включенные в банку. Таким образом, включенные ресурсы будут использоваться как значения по умолчанию. Если вы создадите путь к классу так, чтобы файл .jar появился после папки конфигурации (/etc/myapp
в примере), тогда файлы ресурсов с таким же относительным путём ресурсов в папке конфигурации будут иметь приоритет над теми, которые включены в банку.
У меня была такая же проблема, и решение не с помощью io/file
вообще. Так, например, это рабочий код из моего приложения:
(defn load-md [md]
(->
md
io/resource ;;clojure.java.io
slurp
mp
to-hiccup
html))
Хороший способ сделать это. Обратите внимание, что io/file
возвращает объект java File
, который затем можно передать в slurp
и т.д.:
(ns rescue.core
(:require [clojure.java.io :as io] ))
(def data-file (io/file
(io/resource
"hello.txt" )))
(defn -main []
(println (slurp data-file)) )
Если вы затем выполните следующее в файле проекта lein:
> echo "Hello Resources!" > resources/hello.txt
> lein run
Hello Resources!
и престо! Вы читаете файлы ресурсов через путь к классам.
В нашем случае использование .getPath
или .getFile
было бесполезным. Просто используя input-stream
вместо этого разрешил его.
(defonce classifier
(-> "machine_learning/classifier.model"
resource
input-stream
helper/read))