StreamReader ReadLine抛出处置exception而不是返回null

我正在尝试从FTP服务器获取文本文件,然后逐行读取它,将每行添加到列表中。

我的代码似乎符合以下标准模式:

var response = (FtpWebResponse)request.GetResponse(); using (var responseStream = response.GetResponseStream()) { using (var reader = new StreamReader(responseStream)) { string line; while((line = reader.ReadLine()) != null) { lines.Add(line); } } } 

但是,出于某种原因,当在文件的最后一行调用reader.ReadLine()时,它会抛出一个exception,说“无法访问已处置的对象”。

这真的让我很烦恼。 如果我没记错的话,当没有其他数据时,流的最后一行是null ,对吧?

另外(虽然我不确定这一点),这似乎只在当地发生; 这项服务的现场版本似乎正在顺利进行(虽然有些问题我试图深究)。 我当然不会在日志中看到这个问题。

有人有想法吗?

编辑:这是例外的全文。

 System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.Net.Sockets.NetworkStream'. at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size) at System.Net.FtpDataStream.Read(Byte[] buffer, Int32 offset, Int32 size) at System.IO.StreamReader.ReadBuffer() at System.IO.StreamReader.ReadLine() at CENSORED.CatalogueJobs.Common.FtpFileManager.ReadFile(String fileName, String directory) in C:\\Projects\\CENSORED_CatalogueJobs\\CENSORED.CatalogueJobs.Common\\FtpFileManager.cs:line 104 at CENSORED.CatalogueJobs.CENSOREDDispatchService.CENSOREDDispatchesProcess.d__12.MoveNext() in C:\\Projects\\CENSORED_CatalogueJobs\\CENSORED.CatalogueJobs.CENSOREDDispatches\\CENSOREDDispatchesProcess.cs:line 95" 

类型为“System.ObjectDisposedException”。 对不起审查,例外包含我的客户名称。

编辑2:这是现在的代码,在扩展并删除一层使用后(我想我做得对)。

  var response = (FtpWebResponse)request.GetResponse(); using (var reader = new StreamReader(response.GetResponseStream())) { string line = reader.ReadLine(); while(line != null) { lines.Add(line); line = reader.ReadLine(); } } 

编辑3:稍微宽泛的代码视图(暂时还原为我的理智)。 这基本上是函数中的一切。

  var request = (FtpWebRequest)WebRequest.Create(_settings.Host + directory + fileName); request.Method = WebRequestMethods.Ftp.DownloadFile; request.Credentials = new NetworkCredential(_settings.UserName, _settings.Password); request.UsePassive = false; request.UseBinary = true; var response = (FtpWebResponse)request.GetResponse(); using (var responseStream = response.GetResponseStream()) { using (var reader = new StreamReader(responseStream)) { string line; while((line = reader.ReadLine()) != null) { lines.Add(line); } } } 

检查内部FtpDataStream类的源显示,如果没有更多字节, Read方法将自行关闭流:

  public override int Read(byte[] buffer, int offset, int size) { CheckError(); int readBytes; try { readBytes = m_NetworkStream.Read(buffer, offset, size); } catch { CheckError(); throw; } if (readBytes == 0) { m_IsFullyRead = true; Close(); } return readBytes; } 

Stream.Close()是对Dispose的直接调用:

  public virtual void Close() { /* These are correct, but we'd have to fix PipeStream & NetworkStream very carefully. Contract.Ensures(CanRead == false); Contract.Ensures(CanWrite == false); Contract.Ensures(CanSeek == false); */ Dispose(true); GC.SuppressFinalize(this); } 

不是其他流的方式,例如FileStream.Read 。

看起来StreamReader.ReadLine正在尝试读取更多数据,从而导致exception。 这可能是因为它试图在文件末尾解码UTF8或UTF16字符。

不是从网络流中逐行读取, 最好在读取之前将其复制到MemoryStream或FileStream中,使用Stream.CopyTo

UPDATE

这种行为虽然完全出乎意料,但并非不合理。 以下是基于FTP协议本身, FtpWebRequest.cs和FtpDataStream.cs源以及尝试下载多个文件的痛苦经历的更有根据的猜测。

FtpWebRequest是一个(非常)漏洞,在FTP之上。 FTP是一种面向连接的协议,具有特定命令,如LIST,GET和缺少的MGET。 这意味着,一旦服务器完成向客户端发送数据以响应LIST或GET,它就会返回等待命令。

FtpWebRequest试图通过使每个请求看起来无连接来隐藏它。 这意味着一旦客户端完成从Response流中读取数据,就没有返回的有效状态 – FtpWebResponse命令不能用于发出更多命令。 它也不能用于使用MGET检索多个文件,这是一个很大的痛苦。 毕竟只应该有一个响应流。

使用.NET Core并且需要(温和地说)使用不支持的协议(如SFTP),找到更好的FTP客户端库可能是一个非常好的主意。

我认为正在处理的是响应流。

你不应该在它周围放置一个using语句。 流资源属于FtpWebResponse对象。

尝试删除它,看看问题是否消失。

您也可以通过扩展代码来自己和他人提供服务,以便您可以正确地逐步完成它。 它可能会揭示其他东西:

 using (var reader = new StreamReader(responseStream)) { string line = reader.ReadLine(); while(line != null) { lines.Add(line); line = reader.ReadLine(); } } 

它是另一行代码,它使其更易读,更容易调试。