Search code examples
javaspringspring-mvcspring-bootjava.nio.file

Reading files in a SpringBoot 2.0.1.RELEASE app


I have a SpringBoot 2.0.1.RELEASE mvc application. In the resources folder I have a folder named /elcordelaciutat.

In the controller I have this method to read all the files inside the folder

ClassLoader classLoader = this.getClass().getClassLoader();
        Path configFilePath = Paths.get(classLoader.getResource("elcordelaciutat").toURI());    

        List<String> cintaFileNames = Files.walk(configFilePath)
         .filter(s -> s.toString().endsWith(".txt"))
         .map(p -> p.subpath(8, 9).toString().toUpperCase() + " / " + p.getFileName().toString())
         .sorted()
         .collect(toList());

        return cintaFileNames;

running the app. from Eclipse is working fine, but when I run the app in a Windows Server I got this error:

java.nio.file.FileSystemNotFoundException: null
    at com.sun.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:171)
    at com.sun.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:157)
    at java.nio.file.Paths.get(Unknown Source)
    at 

I unzipped the generated jar file and the folder is there !

and the structure of the folders is

elcordelaciutat/folder1/*.txt
elcordelaciutat/folder2/*.txt
elcordelaciutat/folder3/*.txt

Solution

  • I found the combination of ResourceLoader and ResourcePatternUtils to be the most optimum way of listing/reading files from a classpath resource folder in a Spring Boot application:

    @RestController
    public class ExampleController {
    
        private ResourceLoader resourceLoader;
    
        @Autowired
        public ExampleController(ResourceLoader resourceLoader) {
            this.resourceLoader = resourceLoader;
        }
    
        private List<String> getFiles() throws IOException {
            Resource[] resources = ResourcePatternUtils
                    .getResourcePatternResolver(loader)
                    .getResources("classpath*:elcordelaciutat/*.txt");
    
            return Arrays.stream(resources)
                       .map(p -> p.getFilename().toUpperCase())
                       .sorted()
                       .collect(toList());
    
        }
    }
    

    Updates

    If you want to fetch all the files including the files in subfolders of elcordelaciutat, you need to include the following pattern classpath*:elcordelaciutat/**. This would retrieve the files in the subfolders including the subfolders. Once you get all the resources, filter them based on .txt file extension. Here are the changes you need to make:

    private List<String> getFiles() throws IOException {
        Resource[] resources = ResourcePatternUtils
                .getResourcePatternResolver(loader)
                // notice **
                .getResources("classpath*:elcordelaciutat/**");
    
        return Arrays.stream(resources)
                   .filter(p -> p.getFilename().endsWith(".txt"))
                   .map(p -> {
                       try {
                           String path = p.getURI().toString();
                           String partialPath = path.substring(
                               path.indexOf("elcordelaciutat"));
                           return partialPath;
                       } catch (IOException e) {
                                e.printStackTrace();
                       }
    
                       return "";
                    })
                   .sorted()
                   .collect(toList());
    }
    

    Let's say if you have the following resources folder structure:

    + resources
      + elcordelaciutat
        - FileA.txt
        - FileB.txt
        + a-dir
          - c.txt
          + c-dir
            - d.txt
        + b-dir
          - b.txt
    

    After filtering, the list will contain the following strings:

    • elcordelaciutat/FileA.txt
    • elcordelaciutat/FileB.txt
    • elcordelaciutat/a-dir/c-dir/d.txt
    • elcordelaciutat/a-dir/c.txt
    • elcordelaciutat/b-dir/b.txt

    Note

    When you want to read a resource, you should always use the method getInputStream().