Spring Batch中如何处理FlatFileItemReaderBuilder的空InputStream
解决Spring Batch SFTP读取空InputStream的异常问题
问题根源分析
- 原代码中
try-with-resources块会自动关闭流,导致赋值给外部变量is的流已处于关闭状态,即便后续传递给getFileItemReader也无法正常使用。 - 当所有文件都是空流(
stream.available() == 0)或未找到任何文件时,is会保持null,直接传递给getFileItemReader会触发InputStream must not be null的非法参数异常。 - 方法末尾缺少返回语句,若走到
No Other files found!分支,方法会返回null,不符合Spring Batch对Reader的要求。
具体修复方案
1. 修复流处理的逻辑漏洞
避免在try-with-resources外部持有已关闭的流,直接在有效流的范围内返回Reader;同时跳过空文件,继续尝试下一个文件。
2. 处理无有效文件的场景
当遍历完所有文件都未找到有效流时,返回一个不会读取任何数据的空Reader,而非抛出异常或返回null。
修改后的代码示例
修改sftpReader方法
@Bean @StepScope public FlatFileItemReader<Order> sftpReader() { String sourceDir = reconConfig.getWorkingDir().concat(FOLDER); ConnectionInfo connInfo = util.createConnectionInfo(sourceDir, ""); List<LsEntry> filesToProcess = sftpSvc.pollForFiles(reconConfig.getPattern(), connInfo); log.info("filesToProcess: {}", filesToProcess); for (LsEntry lsEntry : filesToProcess) { String strIB834Filename = lsEntry.getFilename(); log.info("Processing file: {}", strIB834Filename); // 修复原日志打印错误 try (InputStream stream = sftpSvc.getFile(strIB834Filename, connInfo)) { // 校验流是否有效(非空且有数据) if (stream != null && stream.available() > 0) { return getFileItemReader(stream); } else { log.warn("Skipping empty file: {}", strIB834Filename); } } catch (Exception ex) { String msg = String.format("Could not process order file: %s", strIB834Filename); log.error(msg, ex); // 打印完整堆栈而非toString } } log.info("No valid files found to process!"); // 返回空Reader,避免抛出异常 return createEmptyFlatFileItemReader(); }
新增空Reader创建方法
private FlatFileItemReader<Order> createEmptyFlatFileItemReader() { return new FlatFileItemReaderBuilder<Order>() .name(ORDER_ITEM_READER + "_EMPTY") // 使用空InputStream作为资源 .resource(new InputStreamResource(new ByteArrayInputStream(new byte[0]))) .strict(false) .linesToSkip(1) .delimited() .names(names) .lineMapper(lineMapper()) .fieldSetMapper(new BeanWrapperFieldSetMapper<Order>() {{ setTargetType(Order.class); }}) .build(); }
增强getFileItemReader的健壮性
在方法开头增加非空校验,防止意外传入null:
private FlatFileItemReader<Order> getFileItemReader(InputStream inputStream) { Objects.requireNonNull(inputStream, "InputStream must not be null"); // 增加非空校验 return new FlatFileItemReaderBuilder<Order>() .name(ORDER_ITEM_READER) .resource(new InputStreamResource(inputStream)) .strict(false) .linesToSkip(1) .delimited() .names(names) .lineMapper(lineMapper()) .fieldSetMapper(new BeanWrapperFieldSetMapper<Order>() {{ setTargetType(Order.class); }}).build(); }
额外优化建议
- 不要依赖
stream.available()判断文件是否为空:该方法返回的是当前可读取的字节数,并非文件总大小,对于某些流可能不准确。建议读取前几个字节判断,或者让SFTP服务返回文件大小进行校验。 - 若希望在无有效文件时直接终止步骤而非执行空读取,可以抛出
NoWorkFoundException(Spring Batch内置异常),Batch会将步骤标记为COMPLETED而非失败:
// 替换返回空Reader的代码 throw new NoWorkFoundException("No valid files found to process");
内容的提问来源于stack exchange,提问作者JRahman
相关产品推荐
相关产品推荐

