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

时间:2018-02-16 14:37:22

标签: c# ftp stream streamreader ftpwebresponse

我尝试从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);
                    }
                }
            }

3 个答案:

答案 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.csFtpDataStream.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);
        }
    }
}