You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch中如何处理FlatFileItemReaderBuilder的空InputStream

解决Spring Batch SFTP读取空InputStream的异常问题

问题根源分析

  1. 原代码中try-with-resources块会自动关闭流,导致赋值给外部变量is的流已处于关闭状态,即便后续传递给getFileItemReader也无法正常使用。
  2. 当所有文件都是空流(stream.available() == 0)或未找到任何文件时,is会保持null,直接传递给getFileItemReader会触发InputStream must not be null的非法参数异常。
  3. 方法末尾缺少返回语句,若走到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 11:35:30