Spring FlatFile读取CSV最后一行空值引发空指针异常的解决方法
解决CSV读取末尾空行引发NullPointerException的问题
我来帮你搞定这个CSV读取时最后空行引发NPE的问题~
首先分析下问题根源:你遇到的情况大概率是循环逻辑的判断时机不对,或者FlatFileItemReader没有配置过滤空行,导致空行被解析成null的ProductService对象,进而在处理时抛出NPE。你虽然加了while (newPs != null)的判断,但如果判断位置不对,还是会踩坑。
下面给你几个可行的解决方案,按优先级排序:
1. 修正循环读取的逻辑(最直接)
很多时候问题出在循环的写法上,正确的做法是先读取,再判断是否为null,只有非null才处理。比如把读取操作直接放在while的条件里:
public void readPsFile(MultipartFile batchFile) throws Exception { FlatFileItemReader<ProductService> reader = new FlatFileItemReader<>(); // 这里省略你的tokenizer、lineMapper配置... reader.open(new ExecutionContext()); ProductService newPs; // 核心:先读取,判断非null再进入循环处理 while ((newPs = reader.read()) != null) { // 在这里处理打印所有值的逻辑 System.out.println("读取到的数据:" + newPs); } reader.close(); }
这种写法能确保只有当reader.read()返回非null的对象时,才会执行处理逻辑,从根源上避免处理null对象。
2. 配置FlatFileItemReader过滤空行(更优雅)
如果你的CSV文件末尾经常有空行,可以直接给Reader配置一个空行过滤器,提前把空行过滤掉,不用在循环里额外判断。Spring Batch提供了内置的EmptyRecordFilter,专门用来过滤空行:
public void readPsFile(MultipartFile batchFile) throws Exception { FlatFileItemReader<ProductService> reader = new FlatFileItemReader<>(); DelimitedLineTokenizer tokenizer = new DelimitedLineTokenizer(); tokenizer.setNames("你的列名1", "你的列名2"); // 替换成实际CSV列名 tokenizer.setStrict(false); // 允许列数不匹配,避免空行解析报错 BeanWrapperFieldSetMapper<ProductService> mapper = new BeanWrapperFieldSetMapper<>(); mapper.setTargetType(ProductService.class); DefaultLineMapper<ProductService> lineMapper = new DefaultLineMapper<>(); lineMapper.setLineTokenizer(tokenizer); lineMapper.setFieldSetMapper(mapper); reader.setLineMapper(lineMapper); reader.setResource(new ByteArrayResource(batchFile.getBytes())); // 关键:添加空行过滤器 reader.setFilter(new EmptyRecordFilter()); reader.open(new ExecutionContext()); ProductService newPs; while ((newPs = reader.read()) != null) { // 处理逻辑 System.out.println(newPs); } reader.close(); }
这里的EmptyRecordFilter会自动跳过所有空行(包括只含空格的行),这些空行根本不会被解析成ProductService对象,自然不会出现null的情况。另外设置tokenizer.setStrict(false)是为了避免空行解析时因为列数不匹配抛出异常。
3. 自定义过滤器处理特殊空行(灵活)
如果你的空行不是完全空白,而是有空格或者其他特殊字符,可以自定义一个过滤器:
reader.setFilter(new RecordFilter() { @Override public boolean accept(String line) { // 自定义判断逻辑:比如去掉首尾空格后不为空才保留 return StringUtils.isNotBlank(line.trim()); } });
这样可以更灵活地过滤掉不符合要求的行。
内容的提问来源于stack exchange,提问作者Jacel
相关产品推荐
相关产品推荐

