Home > Back-end >  value annotation with wildcards for file name starting with string in spring boot
value annotation with wildcards for file name starting with string in spring boot

Time:12-14

I have below value in my application.properties file

input.file-path=c:\\temp\\data

In that path I have two files:

  • Students_Input_20221212.dat
  • Workers_Input_20221212.dat

is there a way using @Value annotation to get each of the files by using the start with string? Something like below:

@Value("${input.file-path}/Students_Input_*.dat")
private String inputFileStudents;

@Value("${input.file-path}/Workers_Input_*.dat")
private String inputFileWorkers;

So I have:

inputFileStudents = "c:\\temp\\data\\Students_Input_20221212.dat"
inputFileWorkers = "c:\\temp\\data\\Workers_Input_20221212.dat"

CodePudding user response:

Finally found:

[PathMatching]ResourcePatternResolver!

Consider:

  • spring-boot:3 starter (web)
  • properties:
    # existing path (win):
    input.file=C:\\temp\\data
    # file filter (ant path matcher):
    input.students.filter=Students_Input_*.dat
    input.workers.filter=Workers_Input_*.dat
    

Testing time:

// ...
import java.util.Arrays;
// import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.support.ResourcePatternResolver; // !!
// ...
@Bean
InitializingBean patternResolverShowCase( /* <- just a demo ... */
  /*@Autowired*/ ResourcePatternResolver resolver, /* <- autowire/@value "anywhere"(in spring beans)... */
  @Value("${input.file}") String folder, /* <- alternative for @Value: (type-safe) @ConfigurationProperties ;) */
  @Value("${input.students.filter}") String studentsFilter, /* < filter1 */
  @Value("${input.workers.filter}") String workersFilter /* < filter2 */
) {
  return () -> { // <- initializingBean - lambda..
    System.err.println( // <- just a demo...
      Arrays.toString( // ..do whatever you like with:
        resolver.getResources("file:"   folder   "/"   studentsFilter)
      )
    );
    System.err.println(
      Arrays.toString( // ... :
        resolver.getResources("file:"   folder   "/"   workersFilter)
      )
    );
  };
}

With SpEL

Declaring (assigning name and implementation class, otherwise we get a "nameless default"):

import org.springframework.core.io.support.PathMatchingResourcePatternResolver; // !

// ...
@Bean
ResourcePatternResolver rssResolver() {
    return new PathMatchingResourcePatternResolver();
}

..we can also just (SpEL):

import org.springframework.core.io.Resource; // !
// ...
@Value("#{@rssResolver.getResources('file:' '${input.file}' '/' '${input.students.filter}')}") Resource[] studentFiles;
@Value("#{@rssResolver.getResources('file:' '${input.file}' '/' '${input.workers.filter}')}") Resource[] workerFiles;
// do whatever you like with'em (in spring) ...

Explanations

  • above link...
  • properties, path separator handling and adjustment: to your needs/suits.
  • @Value("#{...}") SpEL expression (in a @Value ...).
  • @rssResolver refers to "rssResolver" bean.
  • plus:

Original answer:

Wildcards will be hard (from @Value), but this (wiring a directory into bean context) is easy-peasy:

Properties:

# existing path (win):
myFolder=C:\\temp\\data
# file filter (regex!):
myFileFilter=^test.*\.txt$

Test

@Bean InitializingBean fileResourceShowCase(/* <- just a demo */
    @Value("${myFolder}") FileSystemResource rss1, /* <- org.springframework.core.io */
    @Value("#{T(java.nio.file.Path).of('${myFolder}')}") Path path, /* <- java.nio */
    @Value("#{new java.io.File('${myFolder}')}") File file, /* <- java.io */
    @Value("${myFileFilter}") String filter /* <- custom/extra */
) {
  return () -> { // 3 ways to access java.io.File:
    System.err.println(rss1.getFile());
    System.err.println(path.toFile());
    System.err.println(file);
    // apply the filter:
    File[] files = file.listFiles((f) -> f.getName().matches(filter));
    for (File f : files) {
      System.err.println(f);
    }
  };
}

Explanations:

  • @Value("${myFolder}") FileSystemResource rss1: access the folder as a (spring) FileSystemResource just by it's name (represented by ${myFolder} placeholder). [prefer!]
  • @Value("#{T(java.nio.file.Path).of('${myFolder}')}") Path path: SpEL expression for (static) Path.of(myFolder) (where ${myFolder} is resolved as above).
  • @Value("#{new java.io.File('${myFolder}')}") File file: SpEL expression for: new File(myFolder) ...

Links:

Don'ts

Unfortunately we cannot do this with SpEL:

@Value("""
  #{
    T(java.nio.file.Path).of('${myFolder}')
    .toFile()
    .listFiles(
      (f)->f.getName().matches('${myFileFilter}')
    )
  }
""") File[] files; // -> EL1042E: Problem parsing right operand ;(;(

see: Why doesn't this stream & lambda expression work with SpEL declaration?

Neither this:

@Value("${myFolder}/**") FileSystemResource[] rssArr // -> java.nio.file.InvalidPathException: Illegal char <*> at index ...

... the last (@Value("${myFolder}/**")) approach brought me to: https://www.google.com/search?q=spring antpathmatcher filesystem ;)

  • Related