为何需将输入流两次包装为Apache Commons IO的BOMInputStream?
首先得明确Apache Commons IO的BOMInputStream核心逻辑:它必须在流的起始位置检测并跳过BOM,一旦流的指针已经离开开头(哪怕只移动1个字节),它就没法识别BOM,自然不会跳过,最终BOM会被当成普通文本读进CSV表头。
你遇到的情况,核心原因大概率是以下两种之一:
LocalFileAccess.getInputStream()返回的流已被提前读取
比如这个方法内部可能做了隐式操作:为校验文件存在性、读取元数据,提前调用了read()/skip()之类的方法,或者返回的不是全新的流实例(比如复用了之前打开的流),导致流的指针不在文件最开头。这时候在foo()里用BOMInputStream包装,它从当前指针位置开始检测,找不到BOM就不会跳过,CSV读取器自然会读到BOM前缀。
而在try-with-resources里直接包装getInputStream()的返回值,相当于拿到刚打开、指针在起始位置的流,BOMInputStream能正确检测并跳过BOM,问题就解决了。foo()中包装BOMInputStream后,流被提前消费
如果在把包装后的流传给CSV读取器之前,有其他代码(比如调试日志、临时校验)读取了流内容,哪怕只读1个字节,都会导致BOMInputStream错过BOM位置。而在try-with-resources里直接包装后传给读取器,中间没有额外操作,流始终保持起始状态,就能正常处理BOM。
额外补充:BOMInputStream是一次性检测的——第一次读取流时才会执行BOM检测和跳过逻辑,如果流已被提前消费,这个检测逻辑直接失效。所以必须保证在任何读取操作前,就用BOMInputStream包装原始流,且原始流必须是刚打开、指针在开头的状态。
内容的提问来源于stack exchange,提问作者TheMessik

