/*
Kooboo is a content management system based on ASP.NET MVC framework. Copyright 2009 Yardi Technology Limited.
This program is free software: you can redistribute it and/or modify it under the terms of the
GNU General Public License version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program.
If not, see http://www.kooboo.com/gpl3/.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;
namespace Everest.Library.Net{
public class ResourceDownloader : IDisposable
{
#region Constructors
public ResourceDownloader(string resourceUri)
: this(new Uri(resourceUri))
{
}
public ResourceDownloader(Uri resourceUri)
{
if (resourceUri == null)
{
throw new NullReferenceException("resourceUri can not be null.");
}
this.ResourceUri = resourceUri;
}
#endregion
#region Properties
public Uri ResourceUri
{
get;
private set;
}
private HttpStatusCode _statusCode;
public HttpStatusCode StatusCode
{
get
{
this.InitResourceInfo();
return _statusCode;
}
}
private long _resourceSize;
public long ResourceSize
{
get
{
this.InitResourceInfo();
return _resourceSize;
}
}
private Dictionary<string, object> _items;
public Dictionary<string, object> Items
{
get
{
if (_items == null)
_items = new Dictionary<string, object>();
return _items;
}
}
public Thread AsyncDownloadThread
{
get
{
return _asyncDownloadThread;
}
}
#endregion
#region Progress
private ProgressState _progressState;
public ProgressState ProgressState
{
get
{
return _progressState;
}
}
public event EventHandler<ProgressChangedEventArgs> ProgressChanged;
private void UpdateProgress(long totalBytes, long bytesReaded, bool complete)
{
_progressState = new ProgressState(totalBytes, bytesReaded, complete);
if (this.ProgressChanged != null)
{
this.ProgressChanged.Invoke(this, _progressState);
}
}
public event EventHandler<DownloadErrorEventArgs> Error;
private void ProcessException(Exception ex)
{
if (this.Error != null)
{
this.Error(this, new DownloadErrorEventArgs(ex));
}
}
#endregion
#region ResourceInfo
// A flag
private bool _resourceInfoInited = false;
/// <summary>
/// Initialize the resource infomations.
/// </summary>
/// <param name="force"></param>
private void InitResourceInfo()
{
if (_resourceInfoInited == false)
{
// The request to the web server for file information
HttpWebRequest webRequest = null;
try
{
// Set flag.
_resourceInfoInited = true;
// Create a request to the file we are downloading
webRequest = (HttpWebRequest)WebRequest.Create(this.ResourceUri);
// Set default authentication for retrieving the file
webRequest.Credentials = CredentialCache.DefaultCredentials;
// Retrieve the response from the server
// The response from the web server containing information about the file
using (var webResponse = (HttpWebResponse)webRequest.GetResponse())
{
// Ask the server for the file size and store it
long fileSize = webResponse.ContentLength;
// Set to properties
_resourceSize = fileSize;
_statusCode = webResponse.StatusCode;
}
}
catch (Exception e)
{
ProcessException(e);
}
finally
{
if (webRequest != null)
{
webRequest.Abort();
webRequest = null;
}
}
}
}
#endregion
#region Download
// Async download release stopper flag.
private bool _releaseBlock = false;
// Be canceled flag.
private bool _canceledFlag = false;
// Async download thread.
private Thread _asyncDownloadThread;
/// <summary>
/// Sync download.
/// </summary>
/// <param name="savePath">file path in local driver.</param>
/// <returns>is download success.</returns>
public bool Download(string savePath)
{
// reset flags
_releaseBlock = false;
_canceledFlag = false;
// validations
if (string.IsNullOrEmpty(savePath))
{
_releaseBlock = true;
throw new NullReferenceException("savePath can not be null.");
}
if (!Path.IsPathRooted(savePath))
{
_releaseBlock = true;
throw new Exception("savePath must be a local driver path.");
}
if (this.ResourceSize == 0)
{
_releaseBlock = true;
throw new Exception("the resource size is 0, or can not find the resource.");
}
// download core
using (WebClient wcDownload = new WebClient())
{
try
{
// Initialize the resource infomations.
this.InitResourceInfo();
// Open the URL for download
using (var streamResponse = wcDownload.OpenRead(this.ResourceUri))
{
// Create a new file stream where we will be saving the data (local drive)
using (var streamLocal = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
// It will store the current number of bytes we retrieved from the server
int bytesSize = 0;
// A buffer for storing and writing the data retrieved from the server
byte[] downBuffer = new byte[2048];
// Loop through the buffer until the buffer is empty
while ((bytesSize = streamResponse.Read(downBuffer, 0, downBuffer.Length)) > 0 && _canceledFlag == false)
{
_releaseBlock = true;
// Write the data from the buffer to the local hard drive
streamLocal.Write(downBuffer, 0, bytesSize);
var bytesRead = streamLocal.Length;
var complete = this.ResourceSize == bytesRead;
if (complete)
{
streamLocal.Flush();
streamLocal.Close();
_asyncDownloadThread = null;
}
// Invoke the method that updates the form's label and progress bar
UpdateProgress(this.ResourceSize, bytesRead, complete);
}
}
}
// Download success.
return true;
}
catch (Exception e)
{
// Download failure.
ProcessException(e);
return false;
}
}
}
/// <summary>
/// Async download.
/// </summary>
/// <param name="savePath"></param>
/// <returns></returns>
public Thread BeginDownload(string savePath)
{
if (_asyncDownloadThread == null)
{
_asyncDownloadThread = new Thread(() =>
{
this.Download(savePath);
});
_asyncDownloadThread.Start();
ThreadBlocker.Wait(10 * 1000, () => { return _releaseBlock; });
}
return _asyncDownloadThread;
}
#endregion
#region IDisposable Members
public void Cancel()
{
this.Dispose();
}
public void Dispose()
{
_canceledFlag = true;
if (_asyncDownloadThread != null)
{
_asyncDownloadThread.Abort();
_asyncDownloadThread = null;
}
}
#endregion
}
#region ProgressChangedEventArgs
public class ProgressChangedEventArgs : EventArgs
{
public ProgressChangedEventArgs(long totalBytes, long bytesReaded, bool complete)
{
this.TotalBytes = totalBytes;
this.BytesReaded = bytesReaded;
this.Complete = complete;
}
public long TotalBytes
{
get;
private set;
}
public long BytesReaded
{
get;
private set;
}
public bool Complete
{
get;
private set;
}
public int Percent
{
get
{
return Convert.ToInt32((this.BytesReaded * 100) / this.TotalBytes);
}
}
}
#endregion
#region ProgressState
public class ProgressState : ProgressChangedEventArgs
{
public ProgressState(long totalBytes, long bytesReaded, bool complete)
: base(totalBytes, bytesReaded, complete)
{
}
}
#endregion
public class DownloadErrorEventArgs : EventArgs
{
public DownloadErrorEventArgs(Exception e)
{
Exception = e;
}
public Exception Exception { get; private set; }
}
}
|