FileDownload.cs :  » Network-Clients » SharpDownload » SharpDownload » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Network Clients » SharpDownload 
SharpDownload » SharpDownload » FileDownload.cs
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace SharpDownload{
  /// <summary>
  /// The File Download class controls and monitors progress of downloading a file.
  /// </summary>
  public class FileDownload
  {
    private string localFolder;
    private DownloadInfo downloadInfo = new DownloadInfo();
    private FileDownloadModeEnum fileMode = FileDownloadModeEnum.BinaryMode;
    
    private ArrayList fileParts;

    private bool deletePartsAfterCombine = false;

    private FileDownloadCommandEnum downloadCommand = FileDownloadCommandEnum.NoCommand;
    private const long MIN_SPLIT_SIZE = 20000;
    private const int MAX_FILE_PARTS = 4;

    #region ENUM
    /// <summary>
    /// File download mode type
    /// Used to indicate Binary or ASCII mode
    /// </summary>
    public enum FileDownloadModeEnum
    {
      /// <summary>
      /// Binary mode download
      /// </summary>
      BinaryMode,
      /// <summary>
      /// ASCII mode download
      /// </summary>
      ASCIIMode
    }
    /// <summary>
    /// File download command
    /// Use to send commands to an already running downloading
    /// </summary>
    public enum FileDownloadCommandEnum
    {
      /// <summary>
      /// No command (default)
      /// </summary>
      NoCommand,
      /// <summary>
      /// Pause the download
      /// </summary>
      Pause,
      /// <summary>
      /// Stop the download
      /// </summary>
      Stop
    }
    #endregion
    
    /// <summary>
    /// Default constructor
    /// </summary>
    public FileDownload()
    {
    }

    /// <summary>
    /// Creates a number of file parts
    /// </summary>
    /// <param name="numParts">The number of file parts to create</param>
    public void CreateFileParts(int numParts)
    {
      // Make sure valid number of parts
      if (numParts<=0)
      {
        throw new ApplicationException("Invalid number of file parts.  You must have at least one file part.");
      }

      // Create an array list and fill with FileParts
      fileParts = new ArrayList();
      for (int i=0; i<numParts; i++)
      {
        fileParts.Add(new FilePart(i));
      }
    }

    /// <summary>
    /// Add a file part
    /// </summary>
    public void AddFilePart()
    {
      // Check if arraylist has been set up
      if (fileParts==null)
      {
        // Create a single file part
        CreateFileParts(1);
      }
      else
      {
        // Add a file part to the arraylist
        fileParts.Add(new FilePart(fileParts.Count));
      }
    }

    /// <summary>
    /// Default indexer - returns a FilePart
    /// </summary>
    public FilePart this [int index]
    {
      get
      {
        // Check that index is in bounds
        if (index<0 || index>=fileParts.Count)
        {
          throw new IndexOutOfRangeException();
        }
        
        return (FilePart)fileParts[index];
      }
      set
      {
        // Check that index is in bounds
        if (index<0 || index>=fileParts.Count)
        {
          throw new IndexOutOfRangeException();
        }
        
        fileParts[index] = value;
        
      }
    }

    /// <summary>
    /// Source address property.  Setting this property automatically
    /// decomposes the URL into its component parts
    /// </summary>
    public string SourceAddress
    {
      get
      {
        return downloadInfo.URL;
      }
      set
      {
        downloadInfo.URL = value;
      }
    }

    /// <summary>
    /// Destination folder to download the file to
    /// </summary>
    public string LocalFolder
    {
      get
      {
        return localFolder;
      }
      set
      {
        localFolder = value;
        downloadInfo.LocalPath = value;
      }
    }
    
    /// <summary>
    /// Download mode for FTP downloads
    /// </summary>
    public FileDownloadModeEnum FileDownloadMode
    {
      get
      {
        return fileMode;
      }
      set
      {
        fileMode = value;
      }
    }

    /// <summary>
    /// Number of bytes downloaded.  This is the sum of the file parts
    /// </summary>
    public long BytesDownloaded
    {
      get
      {
        if (fileParts.Count==0)
        {
          return 0;
        }
        else
        {
          long amountDownloaded = 0;
          // Count the amount downloaded
          for (int i=0; i<fileParts.Count; i++)
          {
            // Get the amount downloaded by the file part
            FilePart tempFilePart = (FilePart)fileParts[i];
            amountDownloaded += tempFilePart.BytesDownloaded;
          }
          return amountDownloaded;
        }
      }
    }

    /// <summary>
    /// Number of file parts in this file download
    /// </summary>
    public int NumFileParts
    {
      get
      {
        return fileParts.Count;
      }
    }
    
    /// <summary>
    /// The download info component
    /// </summary>
    public DownloadInfo DownloadInfo
    {
      get
      {
        return downloadInfo;
      }
    }

    /// <summary>
    /// Public property to set/get the download command
    /// </summary>
    public FileDownloadCommandEnum Command
    {
      get
      {
        return downloadCommand;
      }
      set
      {
        downloadCommand = value;
        if (fileParts==null) return;
        foreach (FilePart part in fileParts)
        {
          if (part != null)
          {
            part.Command = value;
          }
        }
      }
    }

    /// <summary>
    /// Asynchronous download call
    /// </summary>
    /// <returns>true if download started successfully; false otherwise</returns>
    public Thread AsyncDownload()
    {

      try
      {
        // Start a new thread
        Thread downloadThread = new Thread(new ThreadStart(StartDownload));
        Debug.WriteLine("FileDownload: Download thread started");
        return downloadThread;
      }
      catch (Exception eThread)
      {
        OnDownloadFailedEvent(new DownloadFailedEventArgs(eThread.Message));
        Debug.WriteLine("FileDownload: Download thread FAILED");
        return null;
      }
    }

    /// <summary>
    /// Start download function
    /// </summary>
    /// <returns></returns>
    public void StartDownload()
    {

      ProtocolClient protoClient = null;

      try
      {
        // Set up the download
        protoClient = ProtocolClient.GetProtocolClient(downloadInfo);

        // Check if logged in 
        if (!protoClient.IsLoggedIn)
        {
          protoClient.Login();
        }

        // Get the file size
        try
        {
          downloadInfo.FileSize = protoClient.GetFileSize(downloadInfo.Directory + "/" + downloadInfo.FileName);
        }
        catch (HttpClient.HttpRedirectException eRedirect)
        {
          // Close the current client
          protoClient.Close();
          // Change the download info to reflect the redirect
          downloadInfo.URL = eRedirect.URL;
          // Let's try the download again
          this.StartDownload();
          return;
        }
      }
      catch (ProtocolClient.ProtocolException e)
      {
        // Exception occurred - exit for now
        // TODO: Fix exception handling here!
        OnDownloadFailedEvent(new DownloadFailedEventArgs(e.Message));
        return;
      }

      // Check if resume is supported
      downloadInfo.ResumeSupported = protoClient.CanResume(downloadInfo.Directory + "/" + downloadInfo.FileName);
      
      // Close temporary protocol client
      protoClient.Close();
      protoClient = null;

      if (downloadInfo.ResumeSupported)
      {
        // Split up the file
        long totalFileSize = downloadInfo.FileSize;
        long partLeftOver = 0;
        long partFileSize = 0;  
        if (totalFileSize>MIN_SPLIT_SIZE)
        {

          // Calculate part sizes
          partLeftOver = totalFileSize % MAX_FILE_PARTS;
          partFileSize = (long)(totalFileSize - partLeftOver) / MAX_FILE_PARTS;

          // Create the file parts
          this.CreateFileParts(MAX_FILE_PARTS);
          for (int i=0; i<MAX_FILE_PARTS; i++)
          {
            // Set the file details
            this[i].SourceFile = downloadInfo.Directory + "/" + downloadInfo.FileName;
            this[i].DestinationFile = downloadInfo.LocalPathFile + "." + i.ToString();

            // Set the offset
            this[i].ByteOffset = (i * partFileSize);

            // Check if this part is the last part
            if (i!=MAX_FILE_PARTS-1)
            {
              // Set part size
              this[i].PartSize = partFileSize;
            }
            else
            {
              // Set part size and "left-over"
              this[i].PartSize = partFileSize + partLeftOver;
              // Flag as last part
              this[i].IsLastPart = true;
            }
          }
        }
        else
        {
          // Don't need to split file up - create one download part
          this.CreateFileParts(1);

          // Set the file details
          this[0].SourceFile = downloadInfo.Directory + "/" + downloadInfo.FileName;
          this[0].DestinationFile = downloadInfo.LocalPathFile + ".0";
          this[0].PartSize = downloadInfo.FileSize;
          this[0].ByteOffset = 0;
        }
      }
      else
      {
        // Server doesn't support resume - download one part
        this.CreateFileParts(1);

        // Set the file details
        this[0].SourceFile = downloadInfo.Directory + "/" + downloadInfo.FileName;
        this[0].DestinationFile = downloadInfo.LocalPathFile + ".0";
        this[0].PartSize = downloadInfo.FileSize;
        this[0].ByteOffset = 0;
      }

      // Loop through the file parts
      for (int i=0; i<this.NumFileParts; i++)
      {
        // Link event handlers
        this[i].PartDownloadStartedEvent += new FilePart.PartDownloadStartedEventHandler(PartDownloadStarted);
        this[i].PartDataReceivedEvent += new FilePart.PartDataReceivedEventHandler(PartDataReceived);
        this[i].PartDownloadStoppedEvent += new FilePart.PartDownloadStoppedEventHandler(PartDownloadStopped);
        this[i].PartDownloadFinishedEvent += new FilePart.PartDownloadFinishedEventHandler(PartDownloadFinished);

        // Start the async download
        this[i].AsyncDownload(downloadInfo);
      }

      // Raise a download started event
      OnDownloadStartedEvent(new DownloadStartedEventArgs(downloadInfo.FileSize));
    }

    #region Event Handlers

    // Part download started event handler
    private void PartDownloadStarted(FilePart sender, FilePart.PartDownloadStartedEventArgs e)
    {
      try
      {
        OnPartDownloadStartedEvent(new PartDownloadStartedEventArgs(e.PartNumber, this[e.PartNumber].PartSize));
      }
      catch (Exception ePartDownloadStarted)
      {
        Debug.WriteLine("FileDownload: PartDownloadStarted event handler error[" + ePartDownloadStarted.Message + "]");
      }
    }

    // Data received event handler
    private void PartDataReceived(FilePart sender, FilePart.PartDataReceivedEventArgs e)
    {
      try
      {
        // Raise a data received event
        OnDataReceivedEvent(new DataReceivedEventArgs(sender.PartNumber, sender.BytesDownloaded));
      }
      catch (Exception ePartDataReceived)
      {
        Debug.WriteLine("FileDownload: PartDataReceived event handler error [" + ePartDataReceived.Message + "]");
      }
    }

    // Data stopped event handler
    private void PartDownloadStopped(FilePart sender, FilePart.PartDownloadStoppedEventArgs e)
    {
      try
      {
        // Raise a data stopped event
        OnDownloadStoppedEvent(new DownloadStoppedEventArgs(sender.PartNumber));
      }
      catch (Exception ePartDataStopped)
      {
        Debug.WriteLine("FileDownload: PartDataStopped event handler error [" + ePartDataStopped.Message + "]");
      }
    }

    // Download finished callback
    private void PartDownloadFinished(FilePart sender, FilePart.PartDownloadFinishedEventArgs e)
    {
      try
      {
        // Raise a download finished event
        OnDownloadFinishedEvent(new DownloadFinishedEventArgs(sender.PartNumber, sender.PartSize));

        // Check if all downloads are complete
        foreach (FilePart part in this.fileParts)
        {
          // Check if any parts haven't finished
          if (!part.IsDownloadComplete)
          {
            return;
          }
        }

        // All downloads complete - combine
        FileStream fsWrite = new FileStream(this.LocalFolder + downloadInfo.FileName, FileMode.CreateNew, FileAccess.Write);
        BinaryWriter w = new BinaryWriter(fsWrite);

        // Loop through file parts
        foreach (FilePart partToCombine in this.fileParts)
        {
          // Create the read stream
          FileStream fsRead = new FileStream(partToCombine.DestinationFile, FileMode.Open, FileAccess.Read);
          BinaryReader r = new BinaryReader(fsRead);
          byte[] buffer = new Byte[fsRead.Length];

          // TODO: Perform read/write op better
          // A large file might produce an enormous buffer, should
          // be done in smaller pieces i.e. buffer[1024]?
          r.Read(buffer, 0, (int)fsRead.Length);
          r.Close();
          fsRead.Close();
          w.Write(buffer);
        }
        w.Close();
        fsWrite.Close();

        // Delete individual file parts
        // TODO: Make delete file parts a choice...
        if (deletePartsAfterCombine)
        {
          // Loop through each file part and remove
          foreach (FilePart partToDelete in this.fileParts)
          {
            File.Delete(partToDelete.DestinationFile);
          }
        }

        // Raise a download complete event
        OnDownloadCompletedEvent(new DownloadCompletedEventArgs());
      }
      catch (Exception ePartDownloadFinished)
      {
        Debug.WriteLine("FileDownload: PartDownloadFinished event handler error [" + ePartDownloadFinished.Message + "]");
      }
    } 

    #endregion

/*    public static void Join(string dest, int bufsize, out int counter, params string[] src)
    {
      counter = 0;
      int origbufsize = bufsize;
      byte[] inbyte = new byte[bufsize];

      BufferedStream output = new BufferedStream(File.Create(dest), bufsize);
      
      foreach (string srcfile in src)
      {
        bufsize = origbufsize;
        BufferedStream input = new BufferedStream(File.OpenRead(srcfile), bufsize);  
        while ( (bufsize = input.Read(inbyte, 0, bufsize)) != 0)
        {
          counter = (int)(input.Position*100/input.Length);
          output.Write(inbyte, 0, bufsize);
        }
        input.Close();
      }
      output.Close();
    }
*/

    #region PartDownloadStarted Event
    /// <summary>
    /// Part Download Started event arguments
    /// </summary>
    public class PartDownloadStartedEventArgs : EventArgs
    {
      private readonly int partNumber;
      private readonly long partSize;
      /// <summary>
      /// PartDownloadStarted event arguments
      /// </summary>
      /// <param name="PartNumber">The download part number that raised the event</param>
      /// <param name="PartSize">The size of the part in Bytes</param>
      public PartDownloadStartedEventArgs (int PartNumber, long PartSize)
      {
        partNumber = PartNumber;
        partSize = PartSize;
      }
      /// <summary>
      /// Public read-only property for the part number
      /// </summary>
      public int PartNumber
      {
        get
        {
          return partNumber;
        }
      }
      /// <summary>
      /// Public read-only property for the size of the part in Bytes
      /// </summary>
      public long PartSize
      {
        get
        {
          return partSize;
        }
      }
    }
    /// <summary>
    /// Delegate for Part Download Started Event
    /// </summary>
    public delegate void PartDownloadStartedEventHandler(PartDownloadStartedEventArgs e);
    /// <summary>
    /// The Part Download Started Event is raised when the part download starts
    /// </summary>
    public event PartDownloadStartedEventHandler PartDownloadStartedEvent;
    /// <summary>
    /// OnPartDownloadStartedEvent
    /// </summary>
    /// <param name="e">PartDownloadStartedEventArgs</param>
    protected virtual void OnPartDownloadStartedEvent(PartDownloadStartedEventArgs e)
    {
      if (PartDownloadStartedEvent != null)
      {
        PartDownloadStartedEvent(e);
      }
    }

    #endregion

    #region DownloadStarted Event
    /// <summary>
    /// Download started event arguments
    /// </summary>
    public class DownloadStartedEventArgs : EventArgs
    {
      private readonly long fileSize;
      /// <summary>
      /// DownloadStarted event arguments
      /// </summary>
      /// <param name="FileSize">The size of the file in Bytes</param>
      public DownloadStartedEventArgs (long FileSize)
      {
        fileSize = FileSize;
      }
      /// <summary>
      /// Public read-only property for the size of the file in Bytes
      /// </summary>
      public long FileSize
      {
        get
        {
          return fileSize;
        }
      }
    }
    /// <summary>
    /// Delegate for Download Started Event
    /// </summary>
    public delegate void DownloadStartedEventHandler(DownloadStartedEventArgs e);
    /// <summary>
    /// The Download Started Event is raised when the download starts
    /// </summary>
    public event DownloadStartedEventHandler DownloadStartedEvent;
    /// <summary>
    /// OnDownloadStartedEvent
    /// </summary>
    /// <param name="e">DownloadStartedEventArgs</param>
    protected virtual void OnDownloadStartedEvent(DownloadStartedEventArgs e)
    {
      if (DownloadStartedEvent != null)
      {
        DownloadStartedEvent(e);
      }
    }

    #endregion

    #region DataReceived Event
    /// <summary>
    /// Data received event arguments
    /// </summary>
    public class DataReceivedEventArgs : EventArgs
    {
      private readonly int partNumber;
      private readonly long bytesDownloaded;
      /// <summary>
      /// DataReceived event arguments
      /// </summary>
      /// <param name="PartNumber">The download part number that raised the event</param>
      /// <param name="BytesDownloaded">The number of bytes downloaded</param>
      public DataReceivedEventArgs (int PartNumber, long BytesDownloaded)
      {
        partNumber = PartNumber;
        bytesDownloaded = BytesDownloaded;
      }
      /// <summary>
      /// Public read-only property 
      /// The download part number that raised the event
      /// </summary>
      public int PartNumber
      {
        get
        {
          return partNumber;
        }
      }
      /// <summary>
      /// Public read-only property
      /// The number of Bytes downloaded
      /// </summary>
      public long BytesDownloaded
      {
        get
        {
          return bytesDownloaded;
        }
      }
    }
    /// <summary>
    /// Delegate for Download Started Event
    /// </summary>
    public delegate void DataReceivedEventHandler(DataReceivedEventArgs e);
    /// <summary>
    /// The Download Started Event is raised when the download starts
    /// </summary>
    public event DataReceivedEventHandler DataReceivedEvent;
    /// <summary>
    /// OnDataReceivedEvent
    /// </summary>
    /// <param name="e">DataReceivedEventArgs</param>
    protected virtual void OnDataReceivedEvent(DataReceivedEventArgs e)
    {
      if (DataReceivedEvent != null)
      {
        DataReceivedEvent(e);
      }
    }

    #endregion

    #region DownloadStopped Event
    /// <summary>
    /// Download stopped event arguments
    /// </summary>
    public class DownloadStoppedEventArgs : EventArgs
    {
      private readonly int partNumber;
      /// <summary>
      /// DownloadStopped event arguments
      /// </summary>
      /// <param name="PartNumber">The part number that raised the event</param>
      /// <param name="PartSize">The size of the file part in Bytes</param>
      public DownloadStoppedEventArgs (int PartNumber)
      {
        partNumber = PartNumber;
      }
      /// <summary>
      /// Public read-only property
      /// The download part number
      /// </summary>
      public int PartNumber
      {
        get
        {
          return partNumber;
        }
      }
    }
    /// <summary>
    /// Delegate for Download Stopped Event
    /// </summary>
    public delegate void DownloadStoppedEventHandler(DownloadStoppedEventArgs e);
    /// <summary>
    /// The Download Sopped Event is raised when the download starts
    /// </summary>
    public event DownloadStoppedEventHandler DownloadStoppedEvent;
    /// <summary>
    /// OnDownloadStoppedEvent
    /// </summary>
    /// <param name="e">DownloadStoppedEventArgs</param>
    protected virtual void OnDownloadStoppedEvent(DownloadStoppedEventArgs e)
    {
      if (DownloadStoppedEvent != null)
      {
        DownloadStoppedEvent(e);
      }
    }

    #endregion

    #region DownloadFinished Event
    /// <summary>
    /// Download started event arguments
    /// </summary>
    public class DownloadFinishedEventArgs : EventArgs
    {
      private readonly int partNumber;
      private readonly long partSize;
      /// <summary>
      /// DownloadFinished event arguments
      /// </summary>
      /// <param name="PartNumber">The part number that raised the event</param>
      /// <param name="PartSize">The size of the file part in Bytes</param>
      public DownloadFinishedEventArgs (int PartNumber, long PartSize)
      {
        partNumber = PartNumber;
        partSize = PartSize;
      }
      /// <summary>
      /// Public read-only property
      /// The download part number
      /// </summary>
      public int PartNumber
      {
        get
        {
          return partNumber;
        }
      }
      /// <summary>
      /// The size of the download part in Bytes
      /// </summary>
      public long PartSize
      {
        get
        {
          return partSize;
        }
      }
    }
    /// <summary>
    /// Delegate for Download Started Event
    /// </summary>
    public delegate void DownloadFinishedEventHandler(DownloadFinishedEventArgs e);
    /// <summary>
    /// The Download Started Event is raised when the download starts
    /// </summary>
    public event DownloadFinishedEventHandler DownloadFinishedEvent;
    /// <summary>
    /// OnDownloadFinishedEvent
    /// </summary>
    /// <param name="e">DownloadFinishedEventArgs</param>
    protected virtual void OnDownloadFinishedEvent(DownloadFinishedEventArgs e)
    {
      if (DownloadFinishedEvent != null)
      {
        DownloadFinishedEvent(e);
      }
    }

    #endregion

    #region DownloadCompleted Event
    /// <summary>
    /// Download started event arguments
    /// </summary>
    public class DownloadCompletedEventArgs : EventArgs
    {
      /// <summary>
      /// Public default constructor
      /// </summary>
      public DownloadCompletedEventArgs ()
      {
      }
    }
    /// <summary>
    /// Delegate for Download Started Event
    /// </summary>
    public delegate void DownloadCompletedEventHandler(DownloadCompletedEventArgs e);
    /// <summary>
    /// The Download Started Event is raised when the download starts
    /// </summary>
    public event DownloadCompletedEventHandler DownloadCompletedEvent;
    /// <summary>
    /// OnDownloadCompletedEvent
    /// </summary>
    /// <param name="e">DownloadCompletedEventArgs</param>
    protected virtual void OnDownloadCompletedEvent(DownloadCompletedEventArgs e)
    {
      if (DownloadCompletedEvent != null)
      {
        DownloadCompletedEvent(e);
      }
    }

    #endregion

    #region DownloadFailed Event
    /// <summary>
    /// Download failed event arguments
    /// </summary>
    public class DownloadFailedEventArgs : EventArgs
    {
      private string message = string.Empty;
      /// <summary>
      /// DownloadFailed event arguments
      /// </summary>
      /// <param name="Message">The reason for the download failure (typically the error message raised)</param>
      public DownloadFailedEventArgs (string Message)
      {
        message = Message;
      }
      /// <summary>
      /// Public read-only error message
      /// </summary>
      public string Message
      {
        get
        {
          return message;
        }
      }
    }
    /// <summary>
    /// Delegate for Download Started Event
    /// </summary>
    public delegate void DownloadFailedEventHandler(DownloadFailedEventArgs e);
    /// <summary>
    /// The Download Started Event is raised when the download starts
    /// </summary>
    public event DownloadFailedEventHandler DownloadFailedEvent;
    /// <summary>
    /// OnDownloadFailedEvent
    /// </summary>
    /// <param name="e">DownloadFailedEventArgs</param>
    protected virtual void OnDownloadFailedEvent(DownloadFailedEventArgs e)
    {
      if (DownloadFailedEvent != null)
      {
        DownloadFailedEvent(e);
      }
    }

    #endregion

  }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.