Kotlin使用CsvMapper解析大CSV文件遇Stream Closed异常如何解决?
问题
服务器生成的CSV报告文件包含10万+行数据,在Kotlin中使用CsvMapper读取该文件时遭遇IOException异常。实现代码如下:
//Declare the mapper object private var csvMapper = CsvMapper().registerModule(KotlinModule()) //Generate Iterator inline fun <reified T> getIterator(fileName: String): MappingIterator<T>? { csvMapper.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE) FileReader(fileName).use { reader -> return csvMapper .readerFor(T::class.java) .without(StreamReadFeature.AUTO_CLOSE_SOURCE) .with(CsvSchema.emptySchema().withHeader()) .readValues<T>(reader) } } //Read the file using iterator fun read(csvFile: String) { val iterator = getIterator<BbMembershipData>(csvFile) if (iterator != null) { while (iterator.hasNext()) { try { val lineElement = iterator.next() println(lineElement) } catch (e: RuntimeJsonMappingException) { println("Iterator Exception: " + e.localizedMessage) } } } }
打印10行数据后抛出如下异常:
Exception in thread "main" java.lang.RuntimeException: Stream closed at com.fasterxml.jackson.databind.MappingIterator._handleIOException(MappingIterator.java:420) at com.fasterxml.jackson.databind.MappingIterator.next(MappingIterator.java:203) at Main$FileProcessor.read(Main.kt:39) at Main.main(Main.kt:54) Caused by: java.io.IOException: Stream closed
请问如何避免该Stream Closed异常?
解决方案
问题核心是**FileReader在use块结束后被自动关闭**,而MappingIterator依赖这个已关闭的流工作,后续调用next()时必然抛出流关闭异常。
修改思路:将文件读取的生命周期与迭代器遍历逻辑绑定,确保流在遍历完成后才关闭。
修改后的代码示例:
private var csvMapper = CsvMapper().registerModule(KotlinModule()) // 调整逻辑,将遍历操作作为参数传入,保证流在遍历结束后才关闭 inline fun <reified T> processCsv(fileName: String, action: (T) -> Unit) { csvMapper.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE) FileReader(fileName).use { reader -> val iterator = csvMapper .readerFor(T::class.java) .without(StreamReadFeature.AUTO_CLOSE_SOURCE) .with(CsvSchema.emptySchema().withHeader()) .readValues<T>(reader) while (iterator.hasNext()) { try { val lineElement = iterator.next() action(lineElement) } catch (e: RuntimeJsonMappingException) { println("Iterator Exception: " + e.localizedMessage) } } } } // 调用示例 fun read(csvFile: String) { processCsv<BbMembershipData>(csvFile) { lineElement -> println(lineElement) } }
关键修改点:
- 移除单独返回
MappingIterator的逻辑,改为在use块内部完成整个CSV遍历操作 - 通过高阶函数
action传入具体行处理逻辑,确保流在遍历结束后才被use自动关闭 - 保留原有异常处理逻辑,避免单条数据解析失败中断整个文件读取
内容的提问来源于stack exchange,提问作者Satyam
相关产品推荐
相关产品推荐

