Ресурсы в приложениях Clojure - PullRequest
52 голосов
/ 04 ноября 2011

Я использую Leiningen в своем проекте Clojure (приложение с графическим интерфейсом) и создал каталог «resources» в корневом каталоге проекта для хранения изображений, которые использует мое приложение.

Когда я запускаю приложение локально во время тестирования, я извлекаю изображения, используя относительный путь "resources / logo.png", и это прекрасно работает. Но когда я создаю Uberjar с использованием Leiningen, Leiningen помещает файлы из папки ресурсов в корневую папку JAR, поэтому мои ссылки на файлы ресурсов больше не работают.

Как правильно обращаться к таким ресурсам с помощью Leiningen?

Ответы [ 7 ]

46 голосов
/ 04 ноября 2011

Предыдущий ответчик (skuro) указал, что мне нужно получить файл из classpath. После еще нескольких копаний, похоже, это решение подходит для моего случая:

(.getFile (clojure.java.io/resource "foo.png"))
30 голосов
/ 02 июня 2013

Просто синтаксический сахар для ответа Кевина Альбрехта:

(require '[clojure.java.io :as io])

(-> "foo.png" io/resource io/file) 
14 голосов
/ 13 июня 2014

Это не является прямым ответом на вопрос OP, но skuro упомянул кое-что в своем последнем абзаце, что может быть весьма полезным, то есть сделать ресурсы доступными на пути к классам во время разработки, но не включать их в релизбанка / uberjar.

Причина в том, что вы можете разместить ресурсы отдельно от jar - например, поместив файлы конфигурации в /etc - и затем связать их с ними во время выполнения, перечислив такие папки ресурсов в classpath.

Определения в project.clj

  • удалить верхний уровень :resource-paths из вашего project.clj;
  • добавить следующие строки в project.clj вверхний уровень:
:profiles {:dev {:resource-paths ["src/main/resources"]}}

Таким образом, ресурсы будут доступны на пути к классам во время разработки (компиляция, тестирование, repl), но не будут включены в выпуски jar.

В качестве альтернативы вы можете объединить некоторые ресурсы с выпуском jar, например значки приложений и графику, но не другие, например файлы конфигурации.В этом случае вы можете разделить папку ресурсов на подкаталоги и объявить те, которые хотите включить на верхнем уровне, а остальные в профиле dev:

:resource-paths ["src/main/resources/icons"
                 "src/main/resources/images"]
:profiles {:dev {:resource-paths ["src/main/resources/config"]}}

Ссылка на ресурсы в коде

Используя вышеупомянутый метод, вы можете ссылаться на ресурсы как во время разработки, так и в процессе производства, просматривая их по пути к классам (как показано Кевин и Валерий ), а не непосредственно нафайловая система:

(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.Таким образом, включенные ресурсы будут использоваться по умолчанию.Если вы настроили classpath так, чтобы файл .jar шел после папки конфигурации (/etc/myapp в примере), то файлы ресурсов с таким же относительным путем к ресурсам в папке конфигурации будут иметь приоритет над файлами, включенными в jar.

14 голосов
/ 04 ноября 2011

Leiningen заимствует соглашение о ресурсах у maven с немного отличающимся расположением папок. Правило гласит, что папка resources должна использоваться в качестве корня пути к классам времени компиляции, что означает, что leiningen прав, когда помещает все файлы в папку resources в корневой папке внутри jar.

Проблема заключается в том, что физическое местоположение! = Местоположение classpath, поэтому, когда первое изменяется при упаковке приложения, второе остается неизменным (classpath:/)

Вам лучше полагаться на местоположение classpath, чтобы найти ресурсы вашей файловой системы, или вынуть их из фляги и поместить в предсказуемую папку, статическую или настраиваемую.

4 голосов
/ 20 марта 2015

У меня была точно такая же проблема, и решение было , а не с использованием io/file вообще.Так, например, это рабочий код из моего приложения:

(defn load-md [md]
  (->
   md
   io/resource ;;clojure.java.io
   slurp
   mp
   to-hiccup
   html))
1 голос
/ 20 января 2015

В нашем случае использование .getPath или .getFile не помогло. Просто используйте input-stream вместо того, чтобы разрешить его.

(defonce classifier
  (-> "machine_learning/classifier.model"
      resource
      input-stream
      helper/read))
1 голос
/ 02 мая 2014

Хороший способ сделать это.Обратите внимание, что 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!

и presto!Вы читаете файлы ресурсов через classpath.

...