StreamReader ReadLine 抛出异常而不是返回 null
StreamReader ReadLine throwing disposed exception rather than returning 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() 时,它抛出异常,显示 "Cannot access a disposed object".
这真让我感到奇怪。如果我没记错的话,当没有更多数据时,流的最后一行是 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"
类型是 "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);
}
}
}
我认为正在处理的是响应流。
你不应该在它周围加上 using
语句。流资源属于 FtpWebResponse
对象。
尝试删除它,看看问题是否消失。
您还可以通过扩展代码来为自己和他人提供服务,以便您可以正确地单步执行它。它可能会揭示其他内容:
using (var reader = new StreamReader(responseStream))
{
string line = reader.ReadLine();
while(line != null)
{
lines.Add(line);
line = reader.ReadLine();
}
}
多了一行代码,可读性更强,更容易调试。
检查内部 FtpDataStream class shows that its 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 正在尝试读取更多数据,导致出现异常。 可能是因为它试图解码文件末尾的 UTF8 或 UTF16 字符。
与其从网络流中逐行读取,不如在读取之前用Stream.CopyTo复制到MemoryStream或FileStream中
更新
这种行为虽然完全出乎意料,但并非没有道理。以下更多是基于 FTP 协议本身、FtpWebRequest.cs and FtpDataStream.cs 来源和尝试下载多个文件的痛苦经历的有根据的猜测。
FtpWebRequest 是 FTP 之上的(非常)漏洞抽象。 FTP 是一个面向连接的协议,具有特定的命令,如 LIST、GET 和缺少的 MGET。这意味着,一旦服务器完成向客户端发送数据以响应 LIST 或 GET,它就会返回等待命令。
FtpWebRequest 试图通过让每个请求看起来是无连接的来隐藏这一点。这意味着一旦客户端完成从响应流中读取数据,就没有有效状态 return 到 - FtpWebResponse 命令 不能 用于发出进一步的命令。它也不能用于通过 MGET 检索 多个 文件,这是一个主要的痛苦。毕竟应该只有 一个 响应流。
随着 .NET Core 和越来越多的需要(委婉地说)使用不受支持的协议,如 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);
}
}
}
我正在尝试从 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() 时,它抛出异常,显示 "Cannot access a disposed object".
这真让我感到奇怪。如果我没记错的话,当没有更多数据时,流的最后一行是 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"
类型是 "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);
}
}
}
我认为正在处理的是响应流。
你不应该在它周围加上 using
语句。流资源属于 FtpWebResponse
对象。
尝试删除它,看看问题是否消失。
您还可以通过扩展代码来为自己和他人提供服务,以便您可以正确地单步执行它。它可能会揭示其他内容:
using (var reader = new StreamReader(responseStream))
{
string line = reader.ReadLine();
while(line != null)
{
lines.Add(line);
line = reader.ReadLine();
}
}
多了一行代码,可读性更强,更容易调试。
检查内部 FtpDataStream class shows that its 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 正在尝试读取更多数据,导致出现异常。 可能是因为它试图解码文件末尾的 UTF8 或 UTF16 字符。
与其从网络流中逐行读取,不如在读取之前用Stream.CopyTo复制到MemoryStream或FileStream中
更新
这种行为虽然完全出乎意料,但并非没有道理。以下更多是基于 FTP 协议本身、FtpWebRequest.cs and FtpDataStream.cs 来源和尝试下载多个文件的痛苦经历的有根据的猜测。
FtpWebRequest 是 FTP 之上的(非常)漏洞抽象。 FTP 是一个面向连接的协议,具有特定的命令,如 LIST、GET 和缺少的 MGET。这意味着,一旦服务器完成向客户端发送数据以响应 LIST 或 GET,它就会返回等待命令。
FtpWebRequest 试图通过让每个请求看起来是无连接的来隐藏这一点。这意味着一旦客户端完成从响应流中读取数据,就没有有效状态 return 到 - FtpWebResponse 命令 不能 用于发出进一步的命令。它也不能用于通过 MGET 检索 多个 文件,这是一个主要的痛苦。毕竟应该只有 一个 响应流。
随着 .NET Core 和越来越多的需要(委婉地说)使用不受支持的协议,如 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);
}
}
}