Lecture d'un fichier de ressources à partir du pot

243

Je voudrais lire une ressource de mon pot comme ceci:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file

et cela fonctionne très bien lors de son exécution dans Eclipse, mais si je l'exporte dans un bocal, exécutez-le, il y a une exception IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

et je ne sais vraiment pas pourquoi, mais avec des tests, j'ai trouvé que si je change

file = new File(getClass().getResource("/file.txt").toURI());

à

file = new File(getClass().getResource("/folder/file.txt").toURI());

alors ça marche à l'opposé (ça marche en pot mais pas en éclipse).

J'utilise Eclipse et le dossier avec mon fichier se trouve dans un dossier de classe.

PrinceCJC
la source
Si vous souhaitez lire des fichiers à partir d'un répertoire dans un bocal avec des numéros pour les fichiers, voir Stackoverflow-Link
Michael Hegner
1
Je ne suis pas sûr que la question initiale concernait Spring. Le lien dans le commentaire précédent fait référence à une réponse spécifique Spring d'une autre question. Je pense que getResourceAsStreamc'est toujours une solution plus simple et plus portable au problème.
Drew MacInnis

Réponses:

398

Plutôt que d'essayer d'adresser la ressource en tant que fichier, demandez simplement au ClassLoader de renvoyer un InputStream pour la ressource à la place via getResourceAsStream :

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

Tant que la file.txtressource est disponible sur le chemin de classe, cette approche fonctionnera de la même manière, que la file.txtressource se trouve dans un classes/répertoire ou à l'intérieur d'un jar.

Le URI is not hierarchicalproduit parce que quelque chose va comme regard l'URI d'une ressource dans un fichier jar ceci: file:/example.jar!/file.txt. Vous ne pouvez pas lire les entrées dans un jar(un zipfichier) comme s'il s'agissait d'un ancien fichier ordinaire .

Cela s'explique bien par les réponses à:

Drew MacInnis
la source
3
Merci, cela a été très utile et le code fonctionne parfaitement, mais j'ai un problème, je dois déterminer s'il InputStreamexiste (comme File.exists()) pour que mon jeu puisse dire s'il faut utiliser le fichier par défaut ou non. Merci.
PrinceCJC
1
Oh et BTW la raison getClass().getResource("**/folder**/file.txt")pour laquelle cela a fonctionné est parce que j'avais ce dossier dans le même répertoire que mon pot :).
PrinceCJC
5
getResourceAsStreamrenvoie null si la ressource n'existe pas, ce qui peut être votre test "existe".
Drew MacInnis
1
BTW, vous avez une faute de frappe: ce devrait être BufferedReader, pas BufferredReader (notez le "r" supplémentaire dans la suite)
mailmindlin
2
Et bien sûr ... n'oubliez pas de fermer inputStream et BufferedReader
Noremac
26

Pour accéder à un fichier dans un bocal, vous avez deux options:

  • Placez le fichier dans la structure de répertoires correspondant au nom de votre package (après avoir extrait le fichier .jar, il doit se trouver dans le même répertoire que le fichier .class), puis accédez-y en utilisant getClass().getResourceAsStream("file.txt")

  • Placez le fichier à la racine (après avoir extrait le fichier .jar, il doit se trouver à la racine), puis accédez-y en utilisant Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

La première option peut ne pas fonctionner lorsque jar est utilisé comme plugin.

Juozas Kontvainis
la source
Merci beaucoup pour cette excellente réponse ... Un autre avantage de la deuxième option est qu'elle fonctionne également à partir de la méthode principale, car getClass () ne fonctionne qu'à partir d'une instance et non à partir de méthodes statiques.
Dan Ortega
6

J'ai eu ce problème avant et j'ai fait un moyen de secours pour le chargement. Fondamentalement, la première méthode fonctionne dans le fichier .jar et la deuxième méthode fonctionne dans eclipse ou un autre IDE.

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}
MForm
la source
3

Jusqu'à présent (décembre 2017), c'est la seule solution que j'ai trouvée qui fonctionne à la fois intérieur et à l'extérieur de l'IDE.

Utilisez PathMatchingResourcePatternResolver

Remarque: cela fonctionne également dans Spring-Boot

Dans cet exemple, je lis certains fichiers situés dans src / main / resources / my_folder :

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}
Naor Bar
la source
3

Le problème est que certaines bibliothèques tierces nécessitent des chemins d'accès aux fichiers plutôt que des flux d'entrée. La plupart des réponses ne traitent pas de ce problème.

Dans ce cas, une solution consiste à copier le contenu de la ressource dans un fichier temporaire. L'exemple suivant utilise jUnit TemporaryFolder.

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }
Navneeth
la source
Cela est passé sous silence. Merci de l'avoir documenté. Je suis curieux de savoir quelles autres options existent sans JUnit.
Alex Moore-Niemi
0

Si vous voulez lire un fichier, je pense qu'il existe toujours une solution similaire:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());
pablo.vix
la source
4
URL.getFile () ne convertit pas une URL en nom de fichier. Il renvoie la partie de l'URL après l'hôte, avec tous les encodages en pourcentage intacts, donc si le chemin contient des caractères non ASCII ou des caractères ASCII non autorisés dans les URL (y compris les espaces), le résultat ne sera pas un nom de fichier existant , même si l'URL est une file:URL.
VGR
48
cela ne fonctionne pas à l'intérieur une fois le programme construit dans un bocal
Akshay Kasar
1
Ne fonctionne pas à partir du fichier JAR sauf si vous convertissez en chaîne et l'enregistrez d'abord localement.
smoosh911
0

Assurez-vous que vous travaillez avec le bon séparateur. J'ai remplacé tout /dans un chemin relatif par un File.separator. Cela a bien fonctionné dans l'IDE, mais n'a pas fonctionné dans le JAR de génération.

Petterson
la source
0

Je pense que cela devrait également fonctionner en java. Le code suivant que j'utilise utilise kotlin.

val resource = Thread.currentThread().contextClassLoader.getResource('resources.txt')
irvifa
la source
0

J'ai trouvé un correctif

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

Remplacez «Main» par la classe java dans laquelle vous l'avez codé. Remplacez «path» par le chemin dans le fichier jar.

par exemple, si vous mettez State1.txt dans le package com.issac.state, puis tapez le chemin d'accès "/ com / issac / state / State1" si vous exécutez Linux ou Mac. Si vous exécutez Windows, tapez le chemin d'accès comme "\ com \ issac \ state \ State1". N'ajoutez pas l'extension .txt au fichier, sauf si l'exception Fichier introuvable se produit.

nul
la source
-1

Vous pouvez utiliser le chargeur de classe qui lira à partir de classpath comme chemin ROOT (sans "/" au début)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
sendon1982
la source
-1

Si vous utilisez Spring, vous pouvez utiliser la méthode suivante pour lire le fichier à partir de src / main / resources:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }
Sujan M.
la source
1
Bienvenue chez SO. Cela ne fournit pas de réponse à la question. Une fois que vous avez une réputation suffisante, vous pourrez commenter n'importe quel message. Vérifiez également ce que puis-je faire à la place .
thewaywewere
Cela supprimera les sauts de ligne dans le fichier!
elad.chen
-1

Pour une raison quelconque, classLoader.getResource()j'ai toujours renvoyé null lorsque j'ai déployé l'application Web sur WildFly 14. obtenir classLoader getClass().getClassLoader()ou Thread.currentThread().getContextClassLoader()retourner null.

getClass().getClassLoader() Le document API dit:

"Renvoie le chargeur de classe pour la classe. Certaines implémentations peuvent utiliser null pour représenter le chargeur de classe bootstrap. Cette méthode retournera null dans ces implémentations si cette classe a été chargée par le chargeur de classe bootstrap."

peut être si vous utilisez WildFly et votre application web essayez ceci

request.getServletContext().getResource()a renvoyé l'url de la ressource. Ici, request est un objet de ServletRequest.

Ram C
la source
-2

Le code ci-dessous fonctionne avec Spring Boot (kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))
Bikash Das
la source