You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

453 lines
12 KiB
C#

using System;
using System.IO;
using System.Net;
namespace AutoUpdater
{
public class FileDownloader
{
private const int downloadBlockSize = 8192;
private bool canceled = false;
private bool paused = false;
private string downloadingTo;
public event DownloadProgressHandler ProgressChanged;
public event DownloadProgressHandler StateChanged;
public event EventHandler DownloadComplete;
static string TEMP_DIR
{
get
{
string localTemp = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData);
localTemp = System.IO.Path.Combine(localTemp , "FileDownloader");
if (!Directory.Exists(localTemp))
Directory.CreateDirectory(localTemp);
return localTemp;
}
}
public static string DownloaderTempDirectory
{
get
{
return TEMP_DIR;
}
}
public string DownloadingTo
{
get { return downloadingTo; }
}
public void Cancel()
{
this.canceled = true;
}
public FileDownloader()
{
}
private void OnDownloadComplete()
{
if (this.DownloadComplete != null)
this.DownloadComplete(this , new EventArgs());
}
public void Download(string url)
{
Download(url , "");
}
/// <summary>
/// 下载URL资源到指定目录
/// </summary>
public void Download(string url , string destFolder)
{
DownloadData data = null;
this.canceled = false;
try
{
if (canceled)
return;
if (destFolder != null & destFolder != string.Empty)
{
if (!Directory.Exists(Path.GetDirectoryName(destFolder)))
Directory.CreateDirectory(Path.GetDirectoryName(destFolder));
}
data = DownloadData.Create(url , destFolder);
//string destFileName = Path.GetFileName(data.Response.ResponseUri.ToString());
string destFileName = Global.Instance.DownloadFileName;
if (Path.GetDirectoryName(destFolder) == destFolder)
this.downloadingTo = Path.GetFullPath(Path.Combine(destFolder , destFileName));
else
this.downloadingTo = Path.GetDirectoryName(destFolder) + "\\" + destFileName;// destFolder;
RaiseStateChanged(data.DownloadState);
byte[] buffer = new byte[downloadBlockSize];
int readCount;
long totalDownloaded = data.StartPoint;
bool gotCanceled = false;
while ((int)(readCount = data.DownloadStream.Read(buffer , 0 , downloadBlockSize)) > 0)
{
if (canceled)
{
gotCanceled = true;
data.Close();
break;
}
while (paused)
{
System.Threading.Thread.Sleep(5);
System.Windows.Forms.Application.DoEvents();
}
totalDownloaded += readCount;
SaveToFile(buffer , readCount , this.downloadingTo);
if (data.IsProgressKnown)
RaiseProgressChanged(totalDownloaded , data.FileSize);
if (canceled)
{
gotCanceled = true;
break;
}
}
if (!gotCanceled)
OnDownloadComplete();
}
finally
{
if (data != null)
data.Close();
}
}
/// <summary>
/// 暂停
/// </summary>
public void Pause()
{
paused = true;
}
/// <summary>
/// 继续
/// </summary>
public void Resume()
{
paused = false;
}
/// <summary>
/// 异步下载数据到当前目录
/// </summary>
public void AsyncDownload(string url)
{
System.Threading.ThreadPool.QueueUserWorkItem(
new System.Threading.WaitCallback(this.WaitCallbackMethod) , new string[] { url , "" });
}
/// <summary>
/// 异步下载数据到目标目录
/// </summary>
public void AsyncDownload(string url , string destFolder)
{
System.Threading.ThreadPool.QueueUserWorkItem(
new System.Threading.WaitCallback(this.WaitCallbackMethod) , new string[] { url , destFolder });
}
private void WaitCallbackMethod(object data)
{
String[] strings = data as String[];
this.Download(strings[0] , strings[1]);
}
private void RaiseStateChanged(string state)
{
if (this.StateChanged != null)
this.StateChanged(this , new DownloadEventArgs(state));
}
private void SaveToFile(byte[] buffer , int count , string fileName)
{
FileStream f = null;
try
{
f = File.Open(fileName , FileMode.Append , FileAccess.Write);
f.Write(buffer , 0 , count);
}
catch (ArgumentException e)
{
throw new ArgumentException(String.Format("尝试存储文件 \"{0}\": {1}" , fileName , e.Message) , e);
}
finally
{
if (f != null)
f.Close();
}
}
private void RaiseProgressChanged(long current , long target)
{
if (this.ProgressChanged != null)
this.ProgressChanged(this , new DownloadEventArgs(target , current));
}
}
public class DownloadData
{
private HttpWebResponse response;
public HttpWebResponse Response
{
get { return response; }
set { response = value; }
}
private Stream stream;
private long size;
private long start;
private bool progressKnown;
public static DownloadData Create(string url , string destFolder)
{
bool progressKnown;
bool resume = false;
long urlSize = GetFileSize(url , out progressKnown);
long startPoint = 0;
HttpWebRequest req = GetRequest(url);
HttpWebResponse response;
try
{
response = (HttpWebResponse)req.GetResponse();
}
catch (Exception e)
{
throw new ArgumentException(String.Format(
"下载错误 \"{0}\": {1}" , url , e.Message) , e);
}
if ((response.ContentType.IndexOf("text/html") > -1) || response.StatusCode == HttpStatusCode.NotFound)
{
throw new ArgumentException(
String.Format("Couldn't download \"{0}\" - a web page was returned from the web server." ,
url));
}
//String fileName = System.IO.Path.GetFileName(response.ResponseUri.ToString());
String fileName = Global.Instance.DownloadFileName;
String downloadTo = string.Empty;
if (Path.GetDirectoryName(destFolder) == destFolder)
downloadTo = Path.GetFullPath(Path.Combine(destFolder , fileName));
else
downloadTo = destFolder;
if (progressKnown && File.Exists(downloadTo))
{
startPoint = new FileInfo(downloadTo).Length;
if (startPoint > urlSize)
File.Delete(downloadTo);
else if (startPoint < urlSize)
{
resume = true;
req.AddRange((int)startPoint);
}
}
else if (File.Exists(downloadTo))
File.Delete(downloadTo);
if (resume)
{
response.Close();
req = GetRequest(url);
req.AddRange((int)startPoint);
response = (HttpWebResponse)req.GetResponse();
}
if (resume && response.StatusCode != HttpStatusCode.PartialContent)
{
File.Delete(downloadTo);
startPoint = 0;
}
return new DownloadData(response , urlSize , startPoint , progressKnown);
}
public static long GetFileSize(string url , out bool progressKnown)
{
HttpWebResponse response = null;
long size = -1;
try
{
response = (HttpWebResponse)GetRequest(url).GetResponse();
size = response.ContentLength;
if (size == -1)
progressKnown = false;
else
progressKnown = true;
}
finally
{
if (response != null)
response.Close();
}
return size;
}
private static HttpWebRequest GetRequest(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = CredentialCache.DefaultCredentials;
return request;
}
private DownloadData(HttpWebResponse response , long size , long start , bool progressKnown)
{
this.response = response;
this.size = size;
this.start = start;
this.stream = null;
this.progressKnown = progressKnown;
}
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 string DownloadState
{
get
{
return this.response.StatusCode.ToString();
}
}
public bool IsProgressKnown
{
get
{
return this.progressKnown;
}
}
public void Close()
{
this.response.Close();
}
}
public class DownloadEventArgs : EventArgs
{
private int percentDone;
private string downloadState;
private long totalFileSize;
public long TotalFileSize
{
get { return totalFileSize; }
set { totalFileSize = value; }
}
private long currentFileSize;
public long CurrentFileSize
{
get { return currentFileSize; }
set { currentFileSize = value; }
}
public DownloadEventArgs(long totalFileSize , long currentFileSize)
{
this.totalFileSize = totalFileSize;
this.currentFileSize = currentFileSize;
this.percentDone = (int)((((double)currentFileSize) / totalFileSize) * 100);
}
public DownloadEventArgs(string state)
{
this.downloadState = state;
}
public DownloadEventArgs(int percentDone , string state)
{
this.percentDone = percentDone;
this.downloadState = state;
}
public int PercentDone
{
get
{
return this.percentDone;
}
}
public string DownloadState
{
get
{
return this.downloadState;
}
}
}
public delegate void DownloadProgressHandler(object sender , DownloadEventArgs e);
}