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?
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"))
Juste un sucre de syntaxe pour la réponse de Kevin Albrecht:
(require '[clojure.Java.io :as io])
(-> "foo.png" io/resource io/file)
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.
:resource-paths
de votre project.clj;: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"]}}
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"))
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.
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.
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))
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))
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.