57 votes

Ressources dans les applications Clojure

J'utilise Leiningen dans mon projet Clojure (une application GUI) et j'ai créé un répertoire "resources" 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 bien. Mais lorsque je construis un uberjar en utilisant Leiningen, Leiningen place les fichiers du dossier des ressources dans le dossier Root du JAR, donc mes références aux fichiers de ressources ne fonctionnent plus.

Quelle est la manière correcte d'accéder à des ressources comme celle-ci en utilisant Leiningen ?

0 votes

Vérifiez ma réponse. J'ai eu le même problème.

0 votes

Existe-t-il un bon exemple de mise en page des ressources ou un exemple de projet qui l'utilise ?

52voto

Kevin Albrecht Points 2527

Le répondant précédent (skuro) a indiqué que je devais récupérer le fichier dans le classpath. Après avoir creusé un peu plus, il semble que ce soit la solution qui fonctionne dans mon cas :

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

7 votes

(.getPath) est plus précis, car (.getFile) ajoute également des paramètres de requête éventuellement existants qui ne devraient pas exister dans les URL de ressources.

0 votes

Pour moi, ça ne marche toujours pas. (io/resource "file.png") renvoie à nil mais file.png est inclus dans le jar fichier. Donc je ne suis pas sûr de ce qui se passe. .getFile donne simplement lieu à un NPE. Il n'y a donc aucun moyen d'utiliser la fonction slurp sur elle.

0 votes

J'ai trouvé mon problème. J'utilise ring pour servir un répertoire à partir de resources/.../ et cela fonctionne pendant le développement. Mais après avoir construit un jar la validation dans le ring.util.response/resource-response échoue. Il renvoie donc nil parce qu'il essaie de vérifier le chemin canonique et il ne correspond pas parce qu'il est dans un jar .

34voto

Valerii Hiora Points 1143

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

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

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

5 votes

Avant que quelqu'un ne commence à slurper sur le fichier, sachez qu'une fois empaqueté dans un jar, cela ne fonctionnera pas. slurp peut fonctionner sur la ressource elle-même : stackoverflow.com/questions/32232662/

21voto

Daniel Dinnyes Points 1273

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 rendre les ressources disponibles sur le classpath pendant le développement, mais ne pas les inclure dans le jar/uberjar de la version.

La raison en est que vous pouvez alors placer les ressources séparément en dehors du jar - par exemple, placer les fichiers de configuration sous /etc - puis de les lier au moment de l'exécution en listant ces dossiers de ressources dans le classpath.

Définitions dans project.clj

  • supprimer le niveau supérieur :resource-paths de votre projet.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 classpath pendant le développement (compiler, tester, repl), mais ne seront pas incluses dans les jars de la version.

En guise d'alternative, vous pourriez vouloir regrouper certaines ressources avec le jar de la version, comme les icônes et les graphiques de l'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 se trouve dans le répertoire dev profil :

: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 pourriez référencer les ressources à la fois pendant le développement et la production de manière uniforme en les recherchant sur le classpath (comme indiqué par Kevin y 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 de l'exécution

Au cours du développement, Leiningen inclurait à la fois le niveau supérieur et le niveau inférieur de l'interface. dev ressources du profil sur le classpath. Pour le runtime publié, vous devrez configurer vous-même le classpath 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 bocal. De cette façon, les ressources incluses seront utilisées par défaut. Si vous configurez le classpath 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 dans le dossier de configuration auront la priorité sur ceux inclus dans le jar.

16voto

skuro Points 9235

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

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

Il vaut mieux se fier à l'emplacement du classpath pour trouver les ressources du système de fichiers, ou les sortir du jar et les placer dans un dossier prévisible, soit statique, soit configurable.

3voto

Alan Thompson Points 325

Belle façon de faire. Notez que io/file renvoie un fichier java File qui peut ensuite être transmis à 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 presto ! Vous lisez les fichiers de ressources via le classpath.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X