Click here to Skip to main content
15,887,135 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hello!

I am trying to read file from FTP line by line:
C#
static void Main(string[] args)
{
var uri = new Uri( ....... );
var networkCredential = new NetworkCredential( ......... );
 
var ftpWebRequest = (FtpWebRequest)WebRequest.Create(uri);
ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
ftpWebRequest.KeepAlive = true;
ftpWebRequest.Timeout = System.Threading.Timeout.Infinite;
 
ftpWebRequest.Credentials = networkCredential;
 
FtpWebResponse ftpWebResponse = null;
 
ftpWebResponse = (FtpWebResponse)ftpWebRequest.GetResponse();
 
using (var responseStream = ftpWebResponse.GetResponseStream())
using (var streamReader = new StreamReader(responseStream, Encoding.Default))
{
    var counter = 0;
    while (true)
    {
        if (streamReader.ReadLine() == null) break;
        counter++;
    }
}
}


Occasionally ReadLine thorws exception:
System.ObjectDisposedException was unhandled
  Message=Cannot access a disposed object.
Object name: 'System.Net.Sockets.NetworkStream'.
  Source=System
  ObjectName=System.Net.Sockets.NetworkStream
  StackTrace:
       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 Training.Program.Main(String[] args) .....
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException: 


I don't understand that is wrong with my code. How to correctly read file from FTP line by line?
Posted
Comments
Sergey Alexandrovich Kryukov 13-May-13 10:45am    
Why doing so at all? And what if the file has no lines at all? Reading lines makes little to no sense, I would say...
—SA
Eugene Efimov 15-May-13 2:58am    
I need to analyze text log located on a remote server accessible only via FTP. Text log structure requires analyzing it line by line. Is there a better way?
Sergey Alexandrovich Kryukov 15-May-13 10:45am    
Why not transferring the whole file (log file, as I understand)? This is what FTP designed for...
—SA
Eugene Efimov 16-May-13 4:42am    
I do transferring the whole file, do i not?
Sergey Alexandrovich Kryukov 16-May-13 9:50am    
I'm not sure...
—SA

The problem is with FtpDataStream.Read(Byte[] buffer, Int32 offset, Int32 size) method. FtpDataStream closes when it is unable to read and gets disposed. You need to create your own custom FtpDataStream and override the Read method and catch ObjectDisposedException error (return 0) to get you out.The uncaught ObjectDisposedException is bubbling up.


Streamreader has a method called ReadBuffer which calls the FtpDataStream.Read(Byte[] buffer, Int32 offset, Int32 size) method. Since FtpDataStream closes and gets disposed , ReadBuffer never gets to be zero which it needs to be for Streamreader.readline to return null

Declare a custom stream class

C#
public class custStream : Stream
{
    internal Stream _stream;

    public custStream(Stream stream)
    {
        _stream = stream;
    }
    public override void WriteByte(byte value)
    {
        _stream.WriteByte(value);
    }

    public override int ReadByte()
    {

        try
        {
            return _stream.ReadByte();
        }
        catch (ObjectDisposedException ode)
        {
            return 0; //
        }
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _stream.Seek(offset, origin);
    }

    public override void Flush()
    {
        _stream.Flush();
    }
    public override long Position
    {
        get
        {
            return _stream.Position;
        }
        set
        {
            _stream.Position = value;
        }
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _stream.Write(buffer, offset, count);
    }

    //this is the one we are really interested in
    public override int Read(byte[] buffer, int offset, int count)
    {
        try
        {
            return _stream.Read(buffer, offset, count);
        }
        catch (ObjectDisposedException ode)
        {
            return 0; //
        }
    }

    public override void SetLength(long value)
    {
        _stream.SetLength(value);
    }



    public override long Length { get { return _stream.Length; } }

    public override bool CanWrite { get { return _stream.CanWrite; } }

    public override bool CanSeek { get { return _stream.CanSeek; } }

    public override bool CanRead { get { return _stream.CanRead; } }

}




In your code change ftpWebResponse.GetResponseStream() to
VB
using (var responseStream = custStream(ftpWebResponse.GetResponseStream()))
 
Share this answer
 
v8
So far, I could not explain what you describe, but… Why not using ftpWebRequest.DownloadData(/*...*/), as recommended in the second code sample on the MSDN help page: http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.aspx[^]?

By the way, you did not really show the working code sample. The code you show won't compile: the curly brackets of first "using" statement are not balanced. Do you through out the non-null result of streamReader.ReadLine()?

—SA
 
Share this answer
 
v2
Comments
Eugene Efimov 21-May-13 9:43am    
Please, check again. All brackets are balanced and code complies fine. The first 'using' statement has no brackets at all, see nested using example here: http://stackoverflow.com/questions/1329739/nested-using-statements-in-c-sharp

Downloading all data at once to a single byte array could be painful for big files, besides question clearly states that line by line processing required, so no need to store whole data at once.

P.S. Current example does not contain part of processing streamReader.ReadLine() non-null result just to make example smaller.
Sergey Alexandrovich Kryukov 21-May-13 9:56am    
I understand about the big files, but the downloading problem by copying the file to a local disk as you real. But this is done in universal way, be reading block-by-block of some constant size (except the last one). Doing it line-by-line is somewhat unpredictable, because you don't know the line size before you read, so it's generally problematic and bad for performance. I would not do it at all.

If your files are logically line-based (like messages), and the files are big, you should rather review your architecture.

—SA
Eugene Efimov 21-May-13 10:06am    
>Doing it line-by-line is somewhat unpredictable, because you don't know the line size before you read, so it's generally problematic and bad for performance. I would not do it at all.
Why is this unpredictable? We need to receive a chunk of data, write it to buffer, scan buffer for newline separators, extract fully-received lines, yield them and remove them from buffer.

In fact, that is that StreamReader does as far I can see. The code above works fine in most cases, and it actually gets all content. Except for throwing exceptions some times in the end of file instead of returning null.

My current workaround is just to ignore ObjectDisposedException if it occurs in ReadLine(). And it works fine, processing date on the fly with unnecessary caching data on local drive. I'm just curious about the reasons of occasionally occuring ObjectDisposedException exceptions.
Sergey Alexandrovich Kryukov 21-May-13 10:31am    
I already explained why, but generally what you describe is a working scenario, I just don't see a whole lot of sense... Bad workaround, anyway. You need to get to the root of the problem. But it's much better not to work with lines.
—SA
Eugene Efimov 22-May-13 9:17am    
> but generally what you describe is a working scenario
Do you have experience working with sockets? Cuz if so, you know there is no way to know the size of incoming packages. And the scenario I described not just "generally working", but a valid and preferable approach (instead of fixing package size and read fix length pachages, what you suggest for in analogy to block-devices).

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900