Le fichier à l'intérieur du pot n'est pas visible pour le printemps

104

Tout

J'ai créé un fichier jar avec le MANIFEST.MF suivant à l'intérieur:

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.3
Created-By: 1.6.0_25-b06 (Sun Microsystems Inc.)
Main-Class: my.Main
Class-Path: . lib/spring-core-3.2.0.M2.jar lib/spring-beans-3.2.0.M2.jar

À sa racine, il y a un fichier appelé my.config qui est référencé dans mon spring-context.xml comme ceci:

<bean id="..." class="...">
    <property name="resource" value="classpath:my.config" />
</bean>

Si j'exécute le jar, tout semble bien sauf le chargement de ce fichier spécifique:

Caused by: java.io.FileNotFoundException: class path resource [my.config] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/D:/work/my.jar!/my.config
        at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:205)
    at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:32)
    at eu.stepman.server.configuration.BeanConfigurationFactoryBean.getObject(BeanConfigurationFactoryBean.java:1)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)
    ... 22 more
  • les classes sont chargées depuis l'intérieur du fichier jar
  • spring et d'autres dépendances sont chargées à partir de pots séparés
  • le contexte de printemps est chargé (nouveau ClassPathXmlApplicationContext ("spring-context / applicationContext.xml"))
  • my.properties est chargé dans PropertyPlaceholderConfigurer ("classpath: my.properties")
  • si je place mon fichier .config en dehors du système de fichiers et que je change l'URL de la ressource en 'file:', tout semble aller bien ...

Des conseils?

BTakacs
la source

Réponses:

211

Si vos fichiers spring-context.xml et my.config sont dans des bocaux différents, vous devrez utiliser classpath*:my.config?

Plus d'infos ici

Assurez-vous également que vous ne l'utilisez resource.getInputStream()pas resource.getFile()lors du chargement à partir d'un fichier jar.

sbk
la source
1
Ils sont dans le même bocal, mais j'ai essayé votre solution avec le même résultat: java.io.FileNotFoundException: la ressource de chemin de classe [classpath *: my.config] ne peut pas être résolue en URL car elle n'existe pas
BTakacs
14
En regardant à nouveau, une partie de votre code d'appel (éventuellement BeanConfigurationFactoryBean) tente de charger un fichier java.io.File. Fichier fait référence aux fichiers du système de fichiers, ce que ne sont pas les entrées d'un fichier jar. Le code appelant doit utiliser resource.getInputStream à la place pour charger à partir d'un fichier jar.
sbk
58
... et c'est la réponse ... Merci! Dans un pot, n'utilisez pas resource.getFile () :-)
BTakacs
2
aucune chance qu'il y ait un "pourquoi?" derrière ne pas utiliser getFile () à l'intérieur d'un pot? Est-ce simplement que le fichier est à l'intérieur du Jar et donc le "fichier" est le fichier jar ??
RockMeetHardplace
8
C'est tout. Un java.io.File représente un fichier sur le système de fichiers, dans une structure de répertoires. Le Jar est un fichier java.io.File. Mais tout ce qui se trouve dans ce fichier est hors de portée de java.io.File. En ce qui concerne java, jusqu'à ce qu'elle soit décompressée, une classe dans un fichier jar n'est pas différente d'un mot dans un document Word.
sbk
50

Je sais que cette question a déjà reçu une réponse. Cependant, pour ceux qui utilisent Spring Boot, ce lien m'a aidé - https://smarterco.de/java-load-file-classpath-spring-boot/

Cependant, resourceLoader.getResource("classpath:file.txt").getFile();cela causait ce problème et le commentaire de sbk:

C'est tout. Un java.io.File représente un fichier sur le système de fichiers, dans une structure de répertoires. Le Jar est un fichier java.io.File. Mais tout ce qui se trouve dans ce fichier est hors de portée de java.io.File. En ce qui concerne java, jusqu'à ce qu'elle soit décompressée, une classe dans un fichier jar n'est pas différente d'un mot dans un document Word.

m'a aidé à comprendre pourquoi utiliser getInputStream() place. Ça marche pour moi maintenant!

Merci!

jmathewt
la source
38

Dans le package spring jar, j'utilise new ClassPathResource(filename).getFile(), ce qui lève l'exception:

ne peut pas être résolu en chemin de fichier absolu car il ne réside pas dans le système de fichiers: jar

Mais utiliser new ClassPathResource(filename).getInputStream()résoudra ce problème. La raison est que le fichier de configuration dans le fichier jar n'existe pas dans l'arborescence de fichiers du système d'exploitation, il doit donc être utilisé getInputStream().

tao zeng
la source
2

J'ai eu un problème similaire lors de l'utilisation de Tomcat6.x et aucun des conseils que j'ai trouvés n'aidait. A la fin j'ai suppriméwork dossier (de Tomcat) et le problème a disparu.

Je sais que c'est illogique mais à des fins de documentation ...

Takacsot
la source
1

La réponse de @sbk est la façon dont nous devrions le faire dans un environnement spring-boot (à part @Value ("$ {classpath *:})), à mon avis. Mais dans mon scénario, cela ne fonctionnait pas si l'exécution de la version autonome jar .. peut-être que j'ai fait quelque chose de mal.

Mais cela peut être une autre façon de faire cela,

InputStream is = this.getClass().getClassLoader().getResourceAsStream(<relative path of the resource from resource directory>);
Abhishek Chatterjee
la source
1

J'avais un problème plus complexe parce que j'ai plus d'un fichier avec le même nom, l'un est dans le pot principal de Spring Boot et d'autres sont dans des pots à l'intérieur du pot de graisse principal. Ma solution consistait à obtenir toutes les ressources du même nom et à obtenir ensuite celle dont j'avais besoin en filtrant par nom de package. Pour obtenir tous les fichiers:

ResourceLoader resourceLoader = new FileSystemResourceLoader();
final Enumeration<URL> systemResources = resourceLoader.getClassLoader().getResources(fileNameWithoutExt + FILE_EXT);
Enrique Jiménez Flores
la source
0

J'avais un problème de chargement récursif des ressources dans mon application Spring, et j'ai constaté que le problème était que je devrais utiliser resource.getInputStream. Voici un exemple montrant comment lire de manière récursive tous les fichiers config/myfilesqui sont des jsonfichiers.

Example.java

private String myFilesResourceUrl = "config/myfiles/**/";
private String myFilesResourceExtension = "json";

ResourceLoader rl = new ResourceLoader();

// Recursively get resources that match. 
// Big note: If you decide to iterate over these, 
// use resource.GetResourceAsStream to load the contents
// or use the `readFileResource` of the ResourceLoader class.
Resource[] resources = rl.getResourcesInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

// Recursively get resource and their contents that match. 
// This loads all the files into memory, so maybe use the same approach 
// as this method, if need be.
Map<Resource,String> contents = rl.getResourceContentsInResourceFolder(myFilesResourceUrl, myFilesResourceExtension);

ResourceLoader.java

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.StreamUtils;

public class ResourceLoader {
  public Resource[] getResourcesInResourceFolder(String folder, String extension) {
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    try {
      String resourceUrl = folder + "/*." + extension;
      Resource[] resources = resolver.getResources(resourceUrl);
      return resources;
    } catch (IOException e) {
      throw new RuntimeException(e);
    }
  }

  public String readResource(Resource resource) throws IOException {
    try (InputStream stream = resource.getInputStream()) {
      return StreamUtils.copyToString(stream, Charset.defaultCharset());
    }
  }

  public Map<Resource, String> getResourceContentsInResourceFolder(
      String folder, String extension) {
    Resource[] resources = getResourcesInResourceFolder(folder, extension);

    HashMap<Resource, String> result = new HashMap<>();
    for (var resource : resources) {
      try {
        String contents = readResource(resource);
        result.put(resource, contents);
      } catch (IOException e) {
        throw new RuntimeException("Could not load resource=" + resource + ", e=" + e);
      }
    }
    return result;
  }
}
Brad Parks
la source
0

J'ai eu le même problème, j'ai fini par utiliser les ressources Guava beaucoup plus pratiques :

Resources.getResource("my.file")
Ahmad Abdelghany
la source