StreamReader ReadLine抛出处置异常而不是返回null [英] StreamReader ReadLine throwing disposed exception rather than returning null

查看:335
本文介绍了StreamReader ReadLine抛出处置异常而不是返回null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图从FTP服务器获取文本文件,然后逐行阅读,并将每行添加到列表中。



我的代码似乎适合这种标准模式:

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

lines.Add(line);
}
}
}

但由于某些原因,当reader.ReadLine()在文件的最后一行被调​​用时,它会抛出一个异常,并说无法访问已处理的对象。

这真的是weirding我出去了。如果我没有记错,那么当没有更多数据时,流的最后一行是 null ,对吧?

我对此并不确定),这似乎只发生在本地;这个服务的实时版本似乎很好(虽然有一些问题我试图达到底部)。我当然不会在我的日志中看到这个问题。



任何人都有想法?

编辑:这里是例外的全文。

  System.ObjectDisposedException:无法访问处置的对象。 
对象名称:'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)在System.IO.StreamReader.ReadBuffer()
在System.IO.StreamReader.ReadLine()
在CENSORED.CatalogueJobs.Common.FtpFileManager.ReadFile(字符串文件名,目录字符串)
在C:\\Projects\\CENSORED_CatalogueJobs\\CENSORED.CatalogueJobs.Common\\FtpFileManager.cs:在CENSORED.CatalogueJobs.CENSOREDDispatchService.CENSOREDDispatchesProcess<线104
;处理> d__12。的MoveNext()在C:\\Projects\\CENSORED_CatalogueJobs\\CENSORED.CatalogueJobs.CENSOREDDispatches\\CENSOREDDispatchesProcess.cs:行95\"

类型是System.ObjectDisposedException。对于审查而言,异常包含我的客户名称。


$ b 编辑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; ((line = reader.ReadLine())!= null)

lines.Add(line);




解决方案

检查内部的来源FtpDataStream 类显示 阅读方法会自动关闭流,如果没有更多的字节:



<$ p (); $ p> 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()
{
/ *这些都是正确的,但我们必须修复PipeStream& NetworkStream非常仔细。
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

更新

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

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

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



使用.NET Core和增加的需求(稍微说明一点)使用不受支持的像SFTP这样的协议,找到一个更好的FTP客户端库可能是一个不错的主意。


I'm trying to fetch a text file from an FTP server, then read it line by line, adding each line to a list.

My code seems to fit the standard pattern for this:

            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);
                    }
                }
            }

But, for some reason, when reader.ReadLine() is called on the very last line of the file, it throws an exception, saying "Cannot access a disposed object".

This is really weirding me out. If I remember correctly, the final line of a stream when there is no further data is null, right?

In addition (while I'm not certain about this), this only seems to be happening locally; the live version of this service seems to be pootling along fine (albeit with some issues I'm trying to get to the bottom of). I certainly don't see this issue in my logs.

Anyone have an ideas?

EDIT: Here's the full text of the exception.

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 "System.ObjectDisposedException". Sorry for censorship, exception contains my client's name.

EDIT 2: Here's the code now, after being expanded out and removing a layer of usings (I think I've done it right).

            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();
                }
            }

EDIT 3: A slightly more wide view of the code (temporarily reverted for my sanity). This is essentially everything in the function.

            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);
                    }
                }
            }

解决方案

Checking the source of the internal FtpDataStream class shows that its Read method will close the stream all by itself if there are no more bytes:

    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() is a direct call to 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);
    }

That's not how other streams, eg FileStream.Read work.

It looks like StreamReader.ReadLine is trying to read more data, resulting in an exception. This could be because it's trying to decode a UTF8 or UTF16 character at the end of the file.

Instead of reading line by line from the network stream, it would be better to copy it into a MemoryStream or FileStream with Stream.CopyTo before reading it

UPDATE

This behaviour, while totally unexpected, isn't unreasonable. The following is more of an educated guess based on the FTP protocol itself, the FtpWebRequest.cs and FtpDataStream.cs sources and painful experience trying to download multiple files.

FtpWebRequest is a (very) leaky abstraction on top of FTP. FTP is a connection oriented protocol with specific commands like LIST, GET and the missing MGET. That means, that once the server has finished sending data to the client in response to LIST or GET, it goes back to waiting for commands.

FtpWebRequest tries to hide this by making each request appear connectionless. This means that once the client finishes reading data from the Response stream there's no valid state to return to - an FtpWebResponse command can't be used to issue further commands. It can't be used to retrieve multiple files with MGET either, which is a major pain. There's only supposed to be one response stream after all.

With .NET Core and an increased need (to put it mildly) to use unsupported protocols like SFTP it may be a very good idea to find a better FTP client library.

这篇关于StreamReader ReadLine抛出处置异常而不是返回null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆