web-dev-qa-db-fra.com

Ressources dans les applications Clojure

J'utilise Leiningen dans mon projet Clojure (une application GUI) et j'ai créé un répertoire "ressources" sous la racine du projet pour contenir les images que mon application utilise.

Lorsque j'exécute mon application localement pendant les tests, je récupère les images en utilisant le chemin relatif "resources/logo.png", et cela fonctionne très bien. Mais lorsque je crée un uberjar à l'aide de Leiningen, Leiningen place les fichiers du dossier des ressources dans le dossier racine du JAR, donc mes références aux fichiers de ressources ne fonctionnent plus.

Quelle est la bonne façon d'accéder à des ressources comme celle-ci en utilisant Leiningen?

52
Kevin Albrecht

Le répondeur précédent (skuro) a souligné que je dois récupérer le fichier depuis le chemin de classe. Après avoir creusé un peu plus, cela semble être la solution qui fonctionne pour mon cas:

(.getFile (clojure.Java.io/resource "foo.png"))
46
Kevin Albrecht

Juste un sucre de syntaxe pour la réponse de Kevin Albrecht:

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

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

Cela ne répond pas directement à la question de l'OP, mais skuro a mentionné quelque chose dans son dernier paragraphe qui peut être très utile, c'est-à-dire pour rendre les ressources disponibles sur le chemin de classe pendant le développement, mais ne pas les inclure dans le pot de sortie/uberjar.

La raison en est que vous pouvez placer les ressources séparément à l'extérieur du bocal, par exemple en plaçant les fichiers de configuration sous /etc - puis les lier à l'exécution en répertoriant ces dossiers de ressources sur le chemin de classe.

Définitions dans project.clj

  • supprimer le niveau supérieur :resource-paths de votre project.clj;
  • ajoutez les lignes suivantes dans le project.clj au niveau supérieur:
:profiles {:dev {:resource-paths ["src/main/resources"]}}

De cette façon, les ressources seront disponibles sur le chemin de classe pendant le développement (compilation, test, repl), mais ne seront pas incluses dans les jars de version.

À titre d'alternative, vous souhaiterez peut-être regrouper certaines des ressources avec le pot de version, comme les icônes et les graphiques d'application, mais pas d'autres, comme les fichiers de configuration. Dans ce cas, vous pouvez diviser le dossier des ressources en sous-répertoires et déclarer ceux que vous souhaitez inclure au niveau supérieur, tandis que le reste sous le profil dev:

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

Référencement des ressources dans le code

En utilisant la méthode ci-dessus, vous pouvez référencer uniformément les ressources pendant le développement et la production en les regardant sur le chemin de classe (comme indiqué par Kevin et Valerii ), et non directement sur le système de fichiers:

(require '[clojure.Java.io :as jio])
(jio/file (jio/resource "relative/path/to/resource.xml"))

Chemin de classe d'exécution

Pendant le développement, Leiningen inclurait à la fois le niveau supérieur et les ressources de profil dev sur le chemin de classe. Pour le runtime publié, vous devrez configurer vous-même le chemin de classe pour le JRE:

Java -cp "/etc/myapp:/usr/local/lib/myapp.jar" myapp.core $*

En outre, vous pouvez également laisser toutes les ressources incluses dans le pot. De cette façon, les ressources incluses seraient utilisées par défaut. Si vous configurez le chemin de classe de sorte que le fichier .jar vienne après le dossier de configuration (/etc/myapp dans l'exemple), les fichiers de ressources ayant le même chemin de ressources relatif sous le dossier de configuration auront priorité sur ceux inclus dans le bocal.

14
Daniel Dinnyes

Leiningen emprunte la convention des ressources à maven, avec des dispositions de dossiers légèrement différentes. La règle stipule que le dossier resources doit être utilisé comme racine de chemin de classe de compilation, ce qui signifie que leiningen a raison de placer tous les fichiers dans le dossier resources à l'emplacement racine à l'intérieur du bocal.

Le problème est que l'emplacement physique! = Emplacement du chemin de classe, de sorte que même si le premier change lorsque vous empaquetez votre application, le second reste le même (classpath:/)

Vous feriez mieux de vous fier à l'emplacement du chemin de classe pour trouver les ressources de votre système de fichiers, ou les retirer du bocal et les placer dans un dossier prévisible, statique ou configurable.

14
skuro

J'ai eu exactement le même problème et la solution est pas en utilisant io/file du tout. Ainsi, par exemple, cela fonctionne avec du code de mon application:

(defn load-md [md]
  (->
   md
   io/resource ;;clojure.Java.io
   Slurp
   mp
   to-hiccup
   html))
4
Adam Arold

Dans notre cas, en utilisant .getPath ou .getFile était inutile. En utilisant simplement input-stream l'a résolu à la place.

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

Belle façon de le faire. Notez que io/file renvoie un Java File objet, qui peut ensuite être passé à Slurp, etc:

(ns rescue.core
  (:require [clojure.Java.io :as io] ))

(def data-file (io/file
                 (io/resource 
                   "hello.txt" )))
(defn -main []
  (println (Slurp data-file)) )

Si vous exécutez ensuite ce qui suit dans le répertoire de votre projet lein:

> echo "Hello Resources!" > resources/hello.txt
> lein run
Hello Resources!

et hop! Vous lisez des fichiers de ressources via le chemin de classe.

1
Alan Thompson