我有一个长期运行的处理进口的流程。导入很长一段时间都很好,但突然突然出现异常,即使我没有改变任何东西。导入文件使用库
NPOI
。
一个进程将文件复制到文件夹:
System.IO.File.Copy(fileNameDownload, path);
几分钟后,另一个进程尝试访问它:
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var xlsxBook = new XSSFWorkbook(fs); // suddenly threw exception
var xlsBook = new HSSFWorkbook(fs);
}
但是访问这个文件流突然抛出了一个新的异常:
Cannot access a closed file
at System.IO.__Error.FileNotOpen()
at System.IO.FileStream.Read(Byte[] array, Int32 offset, Int32 count)
at NPOI.Util.IOUtils.ReadFully(Stream stream, Byte[] b, Int32 off, Int32 len) in C:\github\npoi\main\Util\IOUtils.cs:line 179
at NPOI.POIFS.FileSystem.NPOIFSFileSystem..ctor(Stream stream) in C:\github\npoi\main\POIFS\FileSystem\NPOIFSFileSystem.cs:line 291
重启服务后,错误消失。不过,我想追根究底,以避免将来发生这种情况。
异常看起来来自后台线程。我的猜测是,工作簿使用后台线程来异步保存更改,并且由于底层流已被处理,一些旧对象失败了。
如果工作簿实现了 IDisposable,则需要对其进行处置(除非文档指定不同)。通常喜欢
using var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var xlsxBook = new XSSFWorkbook(fs);
using var xlsBook = new HSSFWorkbook(fs);
如果您要归还作业簿,则需要更多考虑。
对象通常要求任何传递的流在对象的生命周期内都处于活动状态。对象可能也拥有流的所有权,使得处理文件流变得不必要,并且可能适得其反。通常带有“leaveOpen”标志来选择退出。这应该都在文档中指定。
如果文档不清楚,最好保守一点,并按照与创建相反的顺序处理所有内容。例如通过使用包装器:
public class MyWrapper(FileStream Fs, XSSFWorkbook XlsxBook, HSSFWorkbook XlsBook) : IDisposable{
public void Dispose(){
XlsBook.Dispose();
XlsxBook.Dispose();
Fs.Dispose();
}
}