Skip to content

Download and Unpack a zip from an FTP server with recovery

nils måsén edited this page Aug 21, 2019 · 1 revision

Code Reference / Zip Samples / Download and Unpack a zip from an FTP server with recovery

This example extends upon Unpack a zip using ZipInputStream, to provide for a recoverable FTP download and extract.

C#

public class FtpSample
{
    public static void TestZipDownload() 
    {
        // Create the FTP stream that takes care of restarting in event of failure.
        // This will hide any temporary problems from ZipInputStream
        using(var ftpStream = new FtpDownloadStream(
                serverUri: "ftp://www.contoso.com/test.htm", 
                userName: "anonymous", 
                password: "janeDoe@contoso.com"
            )) 
            {
                UnzipFromStream(ftpStream, @"c:\temp\out");
            }
    }

    // Insert the "UnzipFromStream" code from the sample mentioned
}

// Implements a restartable Ftp Download and exposes the result as an uninterrupted stream.
public class FtpDownloadStream : Stream 
{
    private string _serverUri;
    private string _userName;
    private string _password;

    private FtpWebRequest _request;
    private FtpWebResponse _response;
    private Stream _responseStream;
    private int _totalDone;

    public Exception CaughtException;

    public FtpDownloadStream(string serverUri, string userName, string password) 
    {
        _serverUri = serverUri;
        _userName = userName;
        _password = password;
    }

    private void StartFtpDownload() 
    {
        // This can be replaced with the Http equivalents
        _request = WebRequest.Create(_serverUri) as FtpWebRequest;
        _request.Method = WebRequestMethods.Ftp.DownloadFile;
        _request.Credentials = new NetworkCredential(_userName, _password);
        _request.ContentOffset = _totalDone;		// for resume on failure

        _response = (FtpWebResponse)_request.GetResponse();
        _responseStream = _response.GetResponseStream();
        //if (_responseStream != null)
        //	_responseStream.ReadTimeout = 10000;	// Set timeout to 10 seconds for testing.
    }

    public override int Read(byte[] buffer, int offset, int count) 
    {
        int attempts = 0;
        // Adjust the maximum attempts according to your needs
        while (attempts++ < 5) 
        {
            if (_responseStream == null) 
            {
                StartFtpDownload();
            }

            try 
            {
                // This will throw a timeout exception if the connection is interrupted.
                // Will throw null exception if failed to open (start); this will also retry.
                int done = _responseStream.Read(buffer, offset, count);

                _totalDone += done;
                return done;
            }
            catch (Exception ex) 
            {
                CaughtException = ex;
                // Close ftp resources if possible. Set instances to null to force restart. 
                Close();
            }
        }
        return 0;
    }

    public override void Close()
    {
        if (_responseStream != null)
        {
            try
            {
                _responseStream.Close();
                _response.Close();
            }
            catch
            {
                // No action required
            }
        }
        _responseStream = null;
        _response = null;
        _request = null;
    }

    // Implement the Stream methods
    public override void Flush() 
        => throw new NotImplementedException();

    public override long Seek(long offset, SeekOrigin origin)
        => throw new NotImplementedException();

    public override void SetLength(long value)
        => throw new NotImplementedException();

    public override void Write(byte[] buffer, int offset, int count)
        => throw new NotImplementedException(); 

    public override bool CanRead => true;
    public override bool CanSeek => false;
    public override bool CanWrite => false;

    public override long Length => throw new NotImplementedException(); 
    public override long Position {
        get => throw new NotImplementedException(); 
        set => throw new NotImplementedException(); 
    }
}