Подтвердить что ты не робот

Ресурсы в приложениях Clojure

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

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

Каков правильный способ доступа к таким ресурсам, как Leiningen?

4b9b3361

Ответ 1

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

(.getFile (clojure.java.io/resource "foo.png"))

Ответ 2

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

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

(-> "foo.png" io/resource io/file) 

Ответ 3

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

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

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

Ответ 4

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

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

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

  • удалите верхний уровень :resource-paths из вашего проекта .clj;
  • добавьте следующие строки в project.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 в примере), тогда файлы ресурсов с таким же относительным путём ресурсов в папке конфигурации будут иметь приоритет над теми, которые включены в банку.

Ответ 5

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

(defn load-md [md]
  (->
   md
   io/resource ;;clojure.java.io
   slurp
   mp
   to-hiccup
   html))

Ответ 6

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

и престо! Вы читаете файлы ресурсов через путь к классам.

Ответ 7

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

(defonce classifier
  (-> "machine_learning/classifier.model"
      resource
      input-stream
      helper/read))