Spring Batch中如何实现多文件的区分式读取与针对性处理?
Spring Batch多文件读取:区分行所属文件的实现方案
要解决MultiResourceItemReader无法区分行所属文件的问题,核心思路是让每行数据携带所属文件的元信息,后续Processor/Writer就能基于此实现针对文件的处理逻辑。下面是两种可行的落地方案:
方案一:自定义包装类+包装式ItemReader
通过封装MultiResourceItemReader,将当前读取的文件资源与行数据绑定,直接传递给后续组件。
1. 创建文件感知的包装类
定义一个通用包装类,用于存储行数据和对应的文件资源:
public class FileAwareItem<T> { private final T item; private final Resource resource; public FileAwareItem(T item, Resource resource) { this.item = item; this.resource = resource; } // Getter方法 public T getItem() { return item; } public Resource getResource() { return resource; } }
2. 实现文件感知的ItemReader
包装MultiResourceItemReader,利用其resourceCallback监听资源切换事件,记录当前处理的文件,读取每行时将文件信息与行数据封装:
public class FileAwareMultiResourceItemReader<T> implements ItemReader<FileAwareItem<T>> { private final MultiResourceItemReader<T> delegateReader; private Resource currentResource; public FileAwareMultiResourceItemReader(MultiResourceItemReader<T> delegateReader) { this.delegateReader = delegateReader; // 注册资源切换回调,记录当前文件 this.delegateReader.setResourceCallback(resource -> currentResource = resource); } @Override public FileAwareItem<T> read() throws Exception { T rawItem = delegateReader.read(); if (rawItem != null) { return new FileAwareItem<>(rawItem, currentResource); } return null; } }
3. 配置与使用
在Batch配置中替换原有的MultiResourceItemReader,后续Processor/Writer直接处理FileAwareItem:
@Bean public MultiResourceItemReader<YourLinePojo> rawMultiResourceReader() { MultiResourceItemReader<YourLinePojo> reader = new MultiResourceItemReader<>(); reader.setResources(new PathMatchingResourcePatternResolver().getResources("file:/path/to/files/*.csv")); // 设置单行读取的Delegate,比如FlatFileItemReader reader.setDelegate(flatFileItemReader()); return reader; } @Bean public ItemReader<FileAwareItem<YourLinePojo>> fileAwareReader() { return new FileAwareMultiResourceItemReader<>(rawMultiResourceReader()); } // 示例Processor:获取文件信息并处理行数据 @Component public class FileAwareProcessor implements ItemProcessor<FileAwareItem<YourLinePojo>, ProcessedResult> { @Override public ProcessedResult process(FileAwareItem<YourLinePojo> item) throws Exception { String fileName = item.getResource().getFilename(); YourLinePojo lineData = item.getItem(); // 基于文件名和行数据执行业务逻辑 return new ProcessedResult(fileName, lineData.getId(), processLine(lineData)); } }
方案二:利用ExecutionContext传递当前文件
如果不想修改Item结构,可以通过Step的ExecutionContext在资源切换时保存当前文件信息,后续组件从ExecutionContext中读取。
1. 配置MultiResourceItemReader的资源回调
在配置时通过resourceCallback将当前文件存入ExecutionContext:
@Bean public MultiResourceItemReader<YourLinePojo> multiResourceReader(StepExecution stepExecution) { MultiResourceItemReader<YourLinePojo> reader = new MultiResourceItemReader<>(); reader.setResources(/* 你的文件资源 */); reader.setDelegate(flatFileItemReader()); // 资源切换时,将文件名存入ExecutionContext reader.setResourceCallback(resource -> stepExecution.getExecutionContext().putString("currentFile", resource.getFilename()) ); return reader; }
2. 在Processor/Writer中获取当前文件
通过StepExecution获取ExecutionContext中的文件名:
@Component public class FileAwareProcessor implements ItemProcessor<YourLinePojo, ProcessedResult> { @Value("#{stepExecution.executionContext['currentFile']}") private String currentFileName; @Override public ProcessedResult process(YourLinePojo item) throws Exception { // 使用currentFileName区分文件,处理行数据 return new ProcessedResult(currentFileName, item.getId(), processLine(item)); } }
注意:此方案依赖MultiResourceItemReader顺序读取文件的特性(读完一个文件再切换到下一个),如果你的Chunk跨文件读取(极少出现),可能会导致文件信息错误,推荐优先使用方案一。
后续文件针对性处理
拿到每行所属文件后,可在Writer或Step监听器中实现文件级逻辑:
- 比如在Writer中维护
Map<Resource, List<ProcessingStatus>>,记录每个文件的所有行处理结果 - 在
@AfterStep方法中遍历该Map,根据结果执行文件移动、标记状态等操作(如成功移至processed目录,失败移至failed目录)
内容的提问来源于stack exchange,提问作者한인환
相关产品推荐
相关产品推荐

