en Peris
en Peris

Reputation: 1717

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

Upvotes: 4

Views: 4838

Answers (3)

Indra Basak
Indra Basak

Reputation: 7394

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().

Upvotes: 5

Oleksii Kyslytsyn
Oleksii Kyslytsyn

Reputation: 2426

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.StreamUtils;
...
private static final String CURRENT_FILE = "file.txt";

public Resource getCurrentResource() {
    return new ClassPathResource(CURRENT_FILE);
}

Later in client code or in code of some other module code you can even get its bytes:

...
Resource resource = getCurrentResource();
byte[] data = StreamUtils.copyToByteArray(resource.getInputStream());

Upvotes: 3

phisch
phisch

Reputation: 4731

When you start your project from Eclipse, the generated class files and resources are actually just files and folders on your hard drive. This is why it works to iterate over these files with the File class.

When you build a jar, all content is actually ziped and stored in a single archive. You can not access is with file system level tools any more, thus your FileNotFound exception.

Try something like this with the JarURL:

JarURLConnection connection = (JarURLConnection) url.openConnection();
JarFile file = connection.getJarFile();
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
    JarEntry e = entries.nextElement();
    if (e.getName(). endsWith("txt")) {
        // ...
   }
}

Upvotes: 10

Related Questions