我尝试从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()时,它会抛出一个异常,说"无法访问已处置的对象"。
这真的让我大吃一惊。如果我没记错的话,当没有其他数据时,流的最后一行是 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"
Type is&#34; System.ObjectDisposedException&#34;。对不起审查,例外包含我的客户名称。
编辑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);
}
}
}
答案 0 :(得分:3)
检查内部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正在尝试读取更多数据,从而导致异常。这个可能是因为它试图解码文件末尾的UTF8或UTF16字符。
不是从网络流中逐行读取,最好在读取之前将其复制到带有Stream.CopyTo的MemoryStream或FileStream中
<强>更新强>
这种行为虽然完全出乎意料,但并非不合理。以下是基于FTP协议本身,FtpWebRequest.cs和FtpDataStream.cs来源以及尝试下载多个文件的痛苦经历的更有根据的猜测。
FtpWebRequest是FTP上的(非常)漏洞抽象。 FTP是一种面向连接的协议,具有特定命令,如LIST,GET和缺少的MGET。这意味着,一旦服务器完成向客户端发送数据以响应LIST或GET,它就会返回等待命令。
FtpWebRequest尝试通过使每个请求看起来无连接来隐藏。这意味着一旦客户端完成从响应流中读取数据,就没有返回的有效状态 - 使用FtpWebResponse命令 来发出更多命令。它也不能用于使用MGET检索多个文件,这是一个很大的痛苦。毕竟,它只应该是一个响应流。
使用.NET Core并且需要(温和地说)使用不支持的协议(如SFTP),找到更好的FTP客户端库可能是一个非常好的主意。
答案 1 :(得分:0)
我认为正在处理的是响应流。
你不应该在它周围加上using
声明。流资源属于FtpWebResponse
对象。
尝试删除它并查看问题是否消失。
您也可以通过扩展代码为自己和他人提供服务,以便您可以正确地逐步完成。它可能会揭示其他内容:
using (var reader = new StreamReader(responseStream))
{
string line = reader.ReadLine();
while(line != null)
{
lines.Add(line);
line = reader.ReadLine();
}
}
这是另一行代码,它使其更易读,更容易调试。
答案 2 :(得分:0)
我也遇到了这个问题。看起来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);
}
}
}