Spring Batch匹配动态命名带时间戳CSV文件的实现与报错排查
核心错误原因定位
- 初始
IllegalStateException报错:FlatFileItemReader的Resource入参仅支持单个可读取文件资源,直接传入目录路径、且默认开启strict校验,就会触发该报错。 NotReadablePropertyException: Invalid property 'local'报错:SpEL表达式解析逻辑错误。带点的参数名如果不加单引号包裹,会被Spring拆分层级属性,先尝试从jobParameters中读取local属性,找不到对应值就会抛属性不可读的异常。No resources to read报错:未正确触发通配路径的资源匹配逻辑,直接硬编码路径字符串、未将路径解析为Spring可识别的Resource数组,导致没有匹配到任何待处理文件。
可落地的修复实现
1. 调整配置适配通配文件匹配规则
按照文件命名规则NY_Prov_mmddyyyy.csv,在application.properties中配置通配路径,不需要加多余的file:前缀,Spring的资源解析器会自动适配本地文件系统路径:
local.input.file.path.pattern = C:/Users/Desktop/data/in/NY_Prov_*.csv
注意:通配规则要限定前缀为NY_Prov_,避免误匹配目录下的其他无关csv文件。
2. 修正MultiResourceItemReader的实现
之前的实现存在两个关键问题:
- 注入资源的时候SpEL表达式写法错误,触发属性读取异常
- 内层的delegate reader(也就是
FlatFileItemReader)自己绑定了目录路径,就算外层MultiResource解析到了正确的文件,内层reader依然会拿着错误路径读,触发strict模式报错
修正后的完整代码:
@StepScope @Bean public MultiResourceItemReader<Provider> providerMultiResourceItemReader( // 直接注入通配路径配置,Spring会自动遍历路径下匹配的文件,解析为Resource数组 @Value("${local.input.file.path.pattern}") Resource[] matchedResources ) { MultiResourceItemReader<Provider> multiResourceReader = new MultiResourceItemReader<>(); // 设置匹配到的所有待处理文件 multiResourceReader.setResources(matchedResources); // 绑定内层的FlatFileItemReader作为实际单文件读取的处理逻辑 multiResourceReader.setDelegate(providerItemReader()); // 开启strict模式,没有匹配到文件的时候直接抛错,避免静默空跑 multiResourceReader.setStrict(true); return multiResourceReader; } @StepScope @Bean public FlatFileItemReader<Provider> providerItemReader(){ FlatFileItemReader<Provider> reader = new FlatFileItemReader<>(); // 跳过文件表头 reader.setLinesToSkip(1); // 删掉之前硬编码目录路径的逻辑,实际文件资源会由MultiResourceItemReader在循环处理每个文件时自动注入 DefaultLineMapper<Provider> lineMapper = new DefaultLineMapper<>(); DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(); lineMapper.setLineTokenizer(tokenizer); lineMapper.setFieldSetMapper(new ProviderFieldSetMapper()); lineMapper.afterPropertiesSet(); reader.setLineMapper(lineMapper); return reader; }
如果必须从运行时传入的Job参数获取文件通配路径,需要调整SpEL写法,给带点的参数名加单引号包裹,避免被拆成层级属性:
// 错误写法:参数名带点直接放在中括号里,会被SpEL拆分层级属性 // @Value("#{jobParameters[local.input.file.path.pattern]}") Resource[] resources // 正确写法:参数名用单引号包裹,作为字符串key从jobParameters中取值 @Value("#{jobParameters['local.input.file.path.pattern']}") Resource[] resources
3. Step配置的修正逻辑
之前的Step代码直接调用.reader(providerItemReader())的写法有两个问题:
- 跳过了外层的
MultiResourceItemReader,等于没有用多资源读取的逻辑,自然没法处理动态文件名的多文件 - 手动调用
@Bean方法会把Bean的Scope代理给丢失,哪怕标了@StepScope,也会变成单例模式,没法动态接收运行时的文件参数
修正后的Step代码推荐用参数注入的方式,让Spring自动装配对应Bean:
@Bean public Step step2(MultiResourceItemReader<Provider> providerMultiResourceItemReader){ return stepBuilderFactory.get("stepReadLoad") .<Provider, Provider>chunk(1000) // 直接使用外层多资源读取器作为Step的读取入口 .reader(providerMultiResourceItemReader) .writer(providerJdbcBatchItemWriter()) .build(); }
这种写法不需要手动给reader传任何文件参数——所有文件匹配、循环读取、资源切换的逻辑,都会在Step运行时由Spring Batch自动处理,完全适配双月周期运行、文件名带时间戳动态变化的场景。
内容的提问来源于stack exchange,提问作者Praveenks
相关产品推荐
相关产品推荐

