Last active
December 18, 2015 11:48
-
-
Save nguyenvanduocit/5777775 to your computer and use it in GitHub Desktop.
C# file download component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 { } | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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