Skip to content

Instantly share code, notes, and snippets.

@nguyenvanduocit
Last active December 18, 2015 11:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nguyenvanduocit/5777775 to your computer and use it in GitHub Desktop.
Save nguyenvanduocit/5777775 to your computer and use it in GitHub Desktop.
C# file download component
using System.Threading;
namespace System.ComponentModel
{
public class AbortableBackgroundWorker : BackgroundWorker
{
public AbortableBackgroundWorker()
{
this.WorkerSupportsCancellation = true;
this.WorkerReportsProgress = true;
}
/// <summary>
/// Start work in current thread
/// </summary>
/// <param name="objectState"></param>
public void RunWork(object objectState)
{
DoWorkEventArgs args = new DoWorkEventArgs(objectState);
Exception eee = null;
try { OnDoWork(args); }
catch (Exception ex) { eee = ex; }
OnRunWorkerCompleted(new RunWorkerCompletedEventArgs(args.Result, eee, args.Cancel));
}
public object Tag { get; set; }
private Thread workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
workerThread = Thread.CurrentThread;
try
{
base.OnDoWork(e);
workerThread = null;
}
catch (ThreadAbortException)
{
e.Cancel = true; //We must set Cancel property to true!
Thread.ResetAbort(); //Prevents ThreadAbortException propagation
}
}
/// <summary>
/// Kill the background thread
/// </summary>
public void Abort()
{
if (!IsBusy) return;
this.CancelAsync();
try
{
if (workerThread != null)
{
workerThread.Abort();
workerThread = null;
}
}
catch { }
}
}
}
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
namespace Flickrdownloader.component
{
public enum DownloadStatus { None, Downloading, Paused, Success, Failed, Canceled }
/// <summary>
/// Downloads and resumes files from HTTP, FTP, and File (file://) URLS
/// </summary>
public class FileDownloader : AbortableBackgroundWorker
{
// Block size to download is by default 1K.
public static int DownloadBlockSize = 1024 * 200;
private string downloadingTo;
public string FileUrl { get; private set; }
public string DestFolder { get; private set; }
public string DestFileName { get; private set; }
/// <summary>
/// Gets the current DownloadStatus
/// </summary>
public DownloadStatus DownloadStatus { get; private set; }
/// <summary>
/// Gets the current DownloadData
/// </summary>
public DownloadData DownloadData { get; private set; }
/// <summary>
/// Gets the current DownloadSpeed
/// </summary>
public int DownloadSpeed { get; private set; }
/// <summary>
/// Gets the estimate time to finish downloading, the time is in seconds
/// </summary>
public long ETA
{
get
{
if (DownloadData == null || DownloadSpeed == 0) return 0;
long remainBytes = DownloadData.FileSize - totalDownloaded;
return remainBytes / DownloadSpeed;
}
}
public FileDownloader(string FileUrl, string DestFolder, string DestFileName)
{
this.FileUrl = FileUrl;
this.DestFolder = DestFolder;
this.DestFileName = DestFileName;
DoWork += download;
}
/// <summary>
/// Proxy to be used for http and ftp requests.
/// </summary>
public IWebProxy Proxy
{
get { return Helper.InitialProxy(); }
}
public int Progress { get; private set; }
/// <summary>
/// Make the download to Pause
/// </summary>
public void Pause()
{
_pause = true;
}
/// <summary>
/// Make the download to resume
/// </summary>
public void Resume()
{
_pause = false;
}
private bool _pause;
static long SecondTicks = TimeSpan.FromSeconds(1).Ticks;
FileStream fileStream;
long totalDownloaded;
/// <summary>
/// Begin downloading the file at the specified url, and save it to the given folder.
/// </summary>
private void download(object sender, DoWorkEventArgs e)
{
_pause = false;
DownloadStatus = DownloadStatus.Downloading;
OnProgressChanged(new ProgressChangedEventArgs(Progress, null));
DownloadData = DownloadData.Create(FileUrl, DestFolder, this.DestFileName, this.Proxy);
if (string.IsNullOrEmpty(DestFileName))
Path.GetFileName(DownloadData.Response.ResponseUri.ToString());
this.downloadingTo = Path.Combine(DestFolder, DestFileName);
FileMode mode = DownloadData.StartPoint > 0 ? FileMode.Append : FileMode.OpenOrCreate;
fileStream = File.Open(downloadingTo, mode, FileAccess.Write);
byte[] buffer = new byte[DownloadBlockSize];
totalDownloaded = DownloadData.StartPoint;
double totalDownloadedInTime = 0; long totalDownloadedTime = 0;
OnProgressChanged(new ProgressChangedEventArgs(Progress, null));
bool callProgess = true;
while (true)
{
callProgess = true;
if (CancellationPending)
{ DownloadSpeed = Progress = 0; e.Cancel = true; break; }
if (_pause) { DownloadSpeed = 0; DownloadStatus = DownloadStatus.Paused; System.Threading.Thread.Sleep(500); }
else
{
DownloadStatus = DownloadStatus.Downloading;
long startTime = DateTime.Now.Ticks;
int readCount = DownloadData.DownloadStream.Read(buffer, 0, DownloadBlockSize);
if (readCount == 0) break;
totalDownloadedInTime += readCount;
totalDownloadedTime += DateTime.Now.Ticks - startTime;
if (callProgess = totalDownloadedTime >= SecondTicks)
{
DownloadSpeed = (int)(totalDownloadedInTime / TimeSpan.FromTicks(totalDownloadedTime).TotalSeconds);
totalDownloadedInTime = 0; totalDownloadedTime = 0;
}
totalDownloaded += readCount;
fileStream.Write(buffer, 0, readCount);
fileStream.Flush();
}
Progress = (int)(100.0 * totalDownloaded / DownloadData.FileSize);
if (callProgess && DownloadData.IsProgressKnown)
ReportProgress(Progress);
}
ReportProgress(Progress);
}
protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
{
try { if (DownloadData != null) DownloadData.Close(); }
catch { }
try { if (fileStream != null) fileStream.Close(); }
catch { }
if (e.Cancelled)
DownloadStatus = DownloadStatus.Canceled;
else if (e.Error != null) DownloadStatus = DownloadStatus.Failed;
else DownloadStatus = DownloadStatus.Success;
DownloadSpeed = 0;
base.OnRunWorkerCompleted(e);
}
}
/// <summary>
/// Constains the connection to the file server and other statistics about a file
/// that's downloading.
/// </summary>
public class DownloadData
{
private WebResponse response;
private Stream stream;
private long size;
private long start;
private IWebProxy proxy = null;
public static DownloadData Create(string url, string destFolder, String fileName)
{
return Create(url, destFolder, fileName, null);
}
public static DownloadData Create(string url, string destFolder, String fileName, IWebProxy proxy)
{
// This is what we will return
DownloadData downloadData = new DownloadData();
downloadData.proxy = proxy;
long urlSize = downloadData.GetFileSize(url);
downloadData.size = urlSize;
WebRequest req = downloadData.GetRequest(url);
try
{
downloadData.response = (WebResponse)req.GetResponse();
}
catch (Exception e)
{
throw new ArgumentException(String.Format(
"Error downloading \"{0}\": {1}", url, e.Message), e);
}
// Check to make sure the response isn't an error. If it is this method
// will throw exceptions.
ValidateResponse(downloadData.response, url);
String downloadTo = Path.Combine(destFolder, fileName);
// If we don't know how big the file is supposed to be,
// we can't resume, so delete what we already have if something is on disk already.
if (!downloadData.IsProgressKnown && File.Exists(downloadTo))
File.Delete(downloadTo);
if (downloadData.IsProgressKnown && File.Exists(downloadTo))
{
// We only support resuming on http requests
if (!(downloadData.Response is HttpWebResponse))
{
File.Delete(downloadTo);
}
else
{
// Try and start where the file on disk left off
downloadData.start = new FileInfo(downloadTo).Length;
// If we have a file that's bigger than what is online, then something
// strange happened. Delete it and start again.
if (downloadData.start > urlSize)
File.Delete(downloadTo);
else if (downloadData.start < urlSize)
{
// Try and resume by creating a new request with a new start position
downloadData.response.Close();
req = downloadData.GetRequest(url);
((HttpWebRequest)req).AddRange((int)downloadData.start);
downloadData.response = req.GetResponse();
if (((HttpWebResponse)downloadData.Response).StatusCode != HttpStatusCode.PartialContent)
{
// They didn't support our resume request.
File.Delete(downloadTo);
downloadData.start = 0;
}
}
}
}
return downloadData;
}
// Used by the factory method
private DownloadData()
{
}
private DownloadData(WebResponse response, long size, long start)
{
this.response = response;
this.size = size;
this.start = start;
this.stream = null;
}
/// <summary>
/// Checks whether a WebResponse is an error.
/// </summary>
/// <param name="response"></param>
private static void ValidateResponse(WebResponse response, string url)
{
if (response is HttpWebResponse)
{
HttpWebResponse httpResponse = (HttpWebResponse)response;
// If it's an HTML page, it's probably an error page. Comment this
// out to enable downloading of HTML pages.
if (httpResponse.ContentType.Contains("text/html") || httpResponse.StatusCode == HttpStatusCode.NotFound)
{
throw new ArgumentException(
String.Format("Could not download \"{0}\" - a web page was returned from the web server.",
url));
}
}
else if (response is FtpWebResponse)
{
FtpWebResponse ftpResponse = (FtpWebResponse)response;
if (ftpResponse.StatusCode == FtpStatusCode.ConnectionClosed)
throw new ArgumentException(
String.Format("Could not download \"{0}\" - FTP server closed the connection.", url));
}
// FileWebResponse doesn't have a status code to check.
}
/// <summary>
/// Checks the file size of a remote file. If size is -1, then the file size
/// could not be determined.
/// </summary>
/// <param name="url"></param>
/// <param name="progressKnown"></param>
/// <returns></returns>
public long GetFileSize(string url)
{
WebResponse response = null;
long size = -1;
try
{
response = GetRequest(url).GetResponse();
size = response.ContentLength;
}
finally
{
if (response != null)
response.Close();
}
return size;
}
private WebRequest GetRequest(string url)
{
//WebProxy proxy = WebProxy.GetDefaultProxy();
WebRequest request = WebRequest.Create(url);
if (request is HttpWebRequest)
{
request.Credentials = CredentialCache.DefaultCredentials;
Uri result = request.Proxy.GetProxy(new Uri("http://www.google.com"));
}
if (this.proxy != null)
{
request.Proxy = this.proxy;
}
return request;
}
public void Close()
{
this.response.Close();
}
#region Properties
public WebResponse Response
{
get { return response; }
set { response = value; }
}
public Stream DownloadStream
{
get
{
if (this.start == this.size)
return Stream.Null;
if (this.stream == null)
this.stream = this.response.GetResponseStream();
return this.stream;
}
}
public long FileSize
{
get
{
return this.size;
}
}
public long StartPoint
{
get
{
return this.start;
}
}
public bool IsProgressKnown
{
get
{
// If the size of the remote url is -1, that means we
// couldn't determine it, and so we don't know
// progress information.
return this.size > -1;
}
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Flickrdownloader
{
public class FileSizeFormatProvider : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter)) return this;
return null;
}
private const string fileSizeFormat = "fs";
private const Decimal OneKiloByte = 1024M;
private const Decimal OneMegaByte = OneKiloByte * 1024M;
private const Decimal OneGigaByte = OneMegaByte * 1024M;
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (format == null || !format.StartsWith(fileSizeFormat))
{
return defaultFormat(format, arg, formatProvider);
}
if (arg is string)
{
return defaultFormat(format, arg, formatProvider);
}
Decimal size;
try
{
size = Convert.ToDecimal(arg);
}
catch (InvalidCastException)
{
return defaultFormat(format, arg, formatProvider);
}
string suffix;
if (size > OneGigaByte)
{
size /= OneGigaByte;
suffix = "GB/sce";
}
else if (size > OneMegaByte)
{
size /= OneMegaByte;
suffix = "MB/sce";
}
else if (size > OneKiloByte)
{
size /= OneKiloByte;
suffix = "KB/sce";
}
else
{
suffix = "Bytes/sce";
}
string precision = format.Substring(2);
if (String.IsNullOrEmpty(precision)) precision = "2";
return String.Format("{0:N" + precision + "}{1}", size, " " + suffix);
}
private static string defaultFormat(string format, object arg, IFormatProvider formatProvider)
{
IFormattable formattableArg = arg as IFormattable;
if (formattableArg != null)
{
return formattableArg.ToString(format, formatProvider);
}
return arg.ToString();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment