C# StreamReader ReadLine引发已处置异常而不是返回null
我试图从FTP服务器获取一个文本文件,然后逐行读取,将每一行添加到列表中 我的代码似乎符合以下标准模式:C# StreamReader ReadLine引发已处置异常而不是返回null,c#,ftp,stream,streamreader,ftpwebresponse,C#,Ftp,Stream,Streamreader,Ftpwebresponse,我试图从FTP服务器获取一个文本文件,然后逐行读取,将每一行添加到列表中 我的代码似乎符合以下标准模式: var response = (FtpWebResponse)request.GetResponse(); using (var responseStream = response.GetResponseStream()) { using (var reader = new StreamR
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()时,它会抛出一个异常,表示“无法访问已处理的对象”
这真让我感到奇怪。如果我没记错的话,当没有更多数据时,流的最后一行是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.<Process>d__12.MoveNext() in C:\\Projects\\CENSORED_CatalogueJobs\\CENSORED.CatalogueJobs.CENSOREDDispatches\\CENSOREDDispatchesProcess.cs:line 95"
编辑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);
}
}
}
我认为正在处理的是响应流 您不应该在其周围放置
语句。流资源属于FtpWebResponse
对象
试着移除它,看看问题是否消失
您还可以通过扩展代码来为自己和他人提供服务,以便正确地逐步完成它。它可能会揭示出其他一些东西:
using (var reader = new StreamReader(responseStream))
{
string line = reader.ReadLine();
while(line != null)
{
lines.Add(line);
line = reader.ReadLine();
}
}
这又是一行代码,使它更可读,更易于调试。检查内部类的源代码表明,如果没有更多字节,它的方法将自行关闭流:
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;
}
是对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);
}
这不是其他流的工作方式(如)
看起来StreamReader.ReadLine正在尝试读取更多数据,导致异常。这可能是因为它试图解码文件末尾的UTF8或UTF16字符
与其从网络流中逐行读取,不如在读取之前将其复制到内存流或文件流中
更新
这种行为虽然完全出乎意料,但也并非无理。以下是基于FTP协议本身、源代码和尝试下载多个文件的痛苦经历的更有根据的猜测
FtpWebRequest是FTP之上的一个(非常)泄漏的抽象。FTP是一种面向连接的协议,具有特定的命令,如LIST、GET和缺少的MGET。这意味着,一旦服务器完成向客户机发送数据以响应LIST或GET,它就会返回等待命令
FtpWebRequest试图通过使每个请求看起来都没有连接来隐藏这一点。这意味着,一旦客户端从响应流中读取完数据,就没有可返回的有效状态-FtpWebResponse命令不能用于发出进一步的命令。它也不能用MGET检索多个文件,这是一个很大的难题。毕竟应该只有一个响应流
随着.NET内核和使用不受支持的协议(如SFTP)的需求的增加,找到一个更好的FTP客户端库可能是一个非常好的主意。我也遇到了这个问题。看起来FtpDataStream在关闭后将CanRead和CanWrite重置为false。因此,作为一种解决方法,您可以在阅读下一行之前检查CanRead,以避免ObjectDisposedException
using (var responseStream = response.GetResponseStream())
{
using (var reader = new StreamReader(responseStream))
{
string line;
while(responseStream.CanRead && (line = reader.ReadLine()) != null)
{
lines.Add(line);
}
}
}
如果该异常被释放,则读取器将抛出该异常,而不是在最后一行。不知何故,在某个地方您正在访问一个已处理的对象。发布完整异常,包括其调用堆栈。也许错误不在你认为的地方。您可以通过在edit:)中添加的Exception.ToString()
Full Exception来获取调用堆栈,这表明释放的是网络流,而不是读卡器。有错误吗?您是否尝试过在async void
或其他类型的激发并忘记方法中阅读?进行原始调用的代码可能已经完成并在读取器有机会完成FtpWebRequest调用之前处理了响应。这表明您使用了异步代码或迭代器。我不这么认为。我将添加一个编辑,显示之前发生的其余代码。我认为,所有这些都非常基本。一旦读取器完成,处理响应流没有什么错。相反,应该始终对流使用use
。更可能的是,FtpWebResponse
在读者有机会阅读之前就被其他代码处理掉了finish@JuanR无论如何,StreamReader本身将关闭流,除非明确指示它保持打开状态。使用
从流中删除不会产生任何影响effect@PanagiotisKanavos:流是由FtpWebResponse
对象创建的,因此它有责任进行处理。当您拥有流时(例如,当您实例化流时),应始终使用using
。@PanagiotisKanavos:Close!=我非常感谢你!回答得很好。我现在已经按照你的建议进行了重构,一切都很好。再次感谢。@MartinPrikrylm_NetworkStream.Read
填充缓冲区数组而不关心其内容。确定。我想说的是:如果您试图通过将条件更改为来修复此问题!reader.EndOfStream
,如果文本文件中的最后一行没有EOL字符,您仍然会得到此“已处理”异常(由EndOfStream
引发)。
using (var responseStream = response.GetResponseStream())
{
using (var reader = new StreamReader(responseStream))
{
string line;
while(responseStream.CanRead && (line = reader.ReadLine()) != null)
{
lines.Add(line);
}
}
}