齐越消息中心
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

456 lines
15 KiB

using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading;
namespace QYMessageCenter.Common.Http
{
/// <summary>
/// Http下载器
/// </summary>
public class HttpDownloader
{
private IHttpClientFactory httpClientFactory;
public HttpDownloader(IHttpClientFactory httpClientFactory, DownloadSetting dwSetting = null)
{
this.httpClientFactory = httpClientFactory;
complateArgs = new DownloadCompletedEventArgs();
changeArgs = new DownloadProgressChangedEventArgs();
if (dwSetting == null)
dwSetting = new DownloadSetting();
downloadSetting = dwSetting;
buffer = new byte[downloadSetting.BufferSize];
}
~HttpDownloader()
{
this.buffer = null;
this.downloadSetting = null;
this.complateArgs.Error = null;
this.complateArgs = null;
this.changeArgs = null;
}
#region Property
/// <summary>
/// 下载进度变化参数
/// </summary>
private DownloadProgressChangedEventArgs changeArgs;
/// <summary>
/// 下载完成参数
/// </summary>
private DownloadCompletedEventArgs complateArgs;
/// <summary>
/// 下载参数配置类
/// </summary>
private DownloadSetting downloadSetting;
/// <summary>
/// 下载缓冲区
/// </summary>
private byte[] buffer;
#endregion
#region Delegate
public delegate void DownloadProgressChangedEventHandler(object sender, DownloadProgressChangedEventArgs e);
public delegate void DownloadCompletedEventHandler(object sender, DownloadCompletedEventArgs e);
public delegate void ReDownloadEventHandler(object sender, DownloadCompletedEventArgs e);
#endregion
#region Event
/// <summary>
/// 下载进度变化事件
/// </summary>
public event DownloadProgressChangedEventHandler OnDownloadProgressChanged;
/// <summary>
/// 下载完成事件
/// </summary>
public event DownloadCompletedEventHandler OnDownloadComplated;
/// <summary>
/// 自动重下事件
/// </summary>
public event ReDownloadEventHandler OnReDownload;
#endregion
#region Method
/// <summary>
/// 设置下载参数
/// </summary>
/// <param name="dwSetting"></param>
public void SetDownloadSetting(DownloadSetting dwSetting)
{
if (dwSetting != null)
downloadSetting = dwSetting;
}
private void DoProcessChangedEvent(DownloadProgressChangedEventArgs args)
{
OnDownloadProgressChanged?.Invoke(this, args);
}
private void DoCompletedEvent(DownloadCompletedEventArgs args)
{
OnDownloadComplated?.Invoke(this, args);
}
private void DoReDownloadEvent(DownloadCompletedEventArgs args)
{
OnReDownload?.Invoke(this, args);
}
private void ArgsInit()
{
changeArgs.Cancel = false;
complateArgs.Cancelled = false;
complateArgs.Error = null;
}
/// <summary>
/// 获取文件总大小
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public long GetTotalLength(string url)
{
long length = 0;
try
{
using (var httpClient = httpClientFactory.CreateClient())
{
var requestMessage = new HttpRequestMessage(HttpMethod.Head, url);
var httpResponse = httpClient.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead).Result;
if (httpResponse.IsSuccessStatusCode)
length = httpResponse.Content.Headers.ContentLength ?? 0;
}
//var req = (HttpWebRequest)WebRequest.Create(new Uri(url));
//req.Method = "HEAD";
//req.Timeout = 5000;
//var res = (HttpWebResponse)req.GetResponse();
//if (res.StatusCode == HttpStatusCode.OK)
//{
// length = res.ContentLength;
//}
//res.Close();
return length;
}
catch (WebException wex)
{
throw wex;
}
}
private bool DownloadFile(string url, string savePath, long startPosition, long totalSize)
{
long currentReadSize = 0; //当前已经读取的字节数
if (startPosition != 0)
currentReadSize = startPosition;
using (var httpClient = httpClientFactory.CreateClient())
{
try
{
httpClient.Timeout = new TimeSpan(0, 0, 20);
if (currentReadSize != 0 && currentReadSize == totalSize)
{
changeArgs.CurrentSize = changeArgs.TotalSize = totalSize;
return true;
}
if (currentReadSize != 0)
{
try
{
httpClient.DefaultRequestHeaders.Range = new System.Net.Http.Headers.RangeHeaderValue(currentReadSize, totalSize);
}
catch (Exception ex)
{
//http 416
currentReadSize = 0;
}
}
using (var response = httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result)
{
using (var responseStream = response.Content.ReadAsStreamAsync().Result)
{
changeArgs.TotalSize = totalSize;
using (var fs = new FileStream(savePath, currentReadSize == 0 ? FileMode.Create : FileMode.Append, FileAccess.Write))
{
//判断服务器是否支持断点续下
if (currentReadSize != 0 &&
response.StatusCode == HttpStatusCode.PartialContent &&
response.Content.Headers.ContentLength != totalSize)
fs.Seek(startPosition, SeekOrigin.Begin); //支持,从本地文件流末尾进行写入
else
currentReadSize = 0; //不支持,从本地文件流开始进行写入
if (buffer.Length != downloadSetting.BufferSize)
buffer = new byte[downloadSetting.BufferSize];
int size = responseStream.Read(buffer, 0, buffer.Length);
while (size != 0)
{
fs.Write(buffer, 0, size);
if (buffer.Length != downloadSetting.BufferSize)
buffer = new byte[downloadSetting.BufferSize];
size = responseStream.Read(buffer, 0, buffer.Length);
currentReadSize += size; //累计下载大小
//执行下载进度变化事件
changeArgs.CurrentSize = currentReadSize;
DoProcessChangedEvent(changeArgs);
//判断挂起状态
if (changeArgs.Cancel)
return true;
}
//执行下载进度变化事件
changeArgs.CurrentSize = changeArgs.TotalSize;
DoProcessChangedEvent(changeArgs);
fs.Flush(true);
}
//验证远程服务器文件字节数和本地文件字节数是否一致
long localFileSize = 0;
try
{
localFileSize = new FileInfo(savePath).Length;
}
catch (Exception ex)
{
localFileSize = changeArgs.CurrentSize;
}
if (totalSize != localFileSize)
{
complateArgs.Error = new Exception(string.Format("远程文件字节:[{0}]与本地文件字节:[{1}]不一致", totalSize, localFileSize));
}
}
}
}
catch (IOException ex)
{
complateArgs.Error = ex;
}
catch (WebException ex)
{
complateArgs.Error = ex;
}
catch (Exception ex)
{
complateArgs.Error = ex;
}
}
return complateArgs.Error == null;
}
public bool DownloadFile(string url, string saveFolderPath, string saveFileName, long? totalSize)
{
ArgsInit();
var result = false;
//验证文件夹
try
{
if (!Directory.Exists(saveFolderPath))
Directory.CreateDirectory(saveFolderPath);
}
catch (Exception ex)
{
complateArgs.Error = ex;
DoCompletedEvent(complateArgs);
return result;
}
if (totalSize == null || totalSize.Value == 0)
{
try
{
totalSize = GetTotalLength(url);
}
catch (Exception ex)
{
Console.WriteLine("获取远程服务器字节数失败 {0}", ex.Message);
complateArgs.Error = ex;
DoCompletedEvent(complateArgs);
return result;
}
}
string saveFilePath = Path.Combine(saveFolderPath, saveFileName);
long startPosition = 0;
for (var i = 1; i <= downloadSetting.TryCount; i++)
{
if (File.Exists(saveFilePath))
{
try
{
startPosition = new FileInfo(saveFilePath).Length;
}
catch
{
startPosition = 0;
}
}
result = DownloadFile(url, saveFilePath, startPosition, totalSize.Value);
if (result)
{
break;
}
else
{
if (i != downloadSetting.TryCount)
{
complateArgs.TryCount = i + 1;
DoReDownloadEvent(complateArgs);
Thread.Sleep(downloadSetting.SleepTime);
if (complateArgs.Cancelled)
break;
ArgsInit();
}
}
if (complateArgs.Cancelled)
break;
}
DoCompletedEvent(complateArgs);
return result;
}
public byte[] DwonloadBytes(string url)
{
ArgsInit();
using (var httpClient = httpClientFactory.CreateClient())
{
return httpClient.GetByteArrayAsync(url).Result;
}
}
/// <summary>
/// 取消/暂停下载
/// </summary>
public void CancelDownload()
{
this.changeArgs.Cancel = true;
this.complateArgs.Cancelled = true;
}
#endregion
}
/// <summary>
/// 下载进度变化参数
/// </summary>
public class DownloadProgressChangedEventArgs
{
public DownloadProgressChangedEventArgs()
{
ProgressPercentage = 0.0;
Cancel = false;
}
/// <summary>
/// 下载进度百分比
/// </summary>
public double ProgressPercentage;
private long currentSize;
/// <summary>
/// 当前下载总大小
/// </summary>
public long CurrentSize
{
get { return currentSize; }
set
{
currentSize = value;
this.ProgressPercentage = Math.Round((CurrentSize * 1.0 / TotalSize) * 100, 2);
}
}
/// <summary>
/// 文件总大小
/// </summary>
public long TotalSize;
/// <summary>
/// 取消状态
/// </summary>
public bool Cancel;
}
/// <summary>
/// 下载完成参数
/// </summary>
public class DownloadCompletedEventArgs
{
public DownloadCompletedEventArgs()
{
Cancelled = false;
Error = null;
}
/// <summary>
/// 下载异常
/// </summary>
public Exception Error;
/// <summary>
/// 重试次数
/// </summary>
public int TryCount;
/// <summary>
/// 下载操作是否被取消 【取消则为true;默认false】
/// </summary>
public bool Cancelled;
}
public class DownloadSetting
{
public DownloadSetting()
{
this.BufferSize = 1024 * 1024 * 1;
this.TryCount = 5;
this.SleepTime = 5000;
}
/// <summary>
///
/// </summary>
/// <param name="bufferSize"></param>
/// <param name="tryCount"></param>
/// <param name="sleepTime">毫秒</param>
public DownloadSetting(int bufferSize, int tryCount, int sleepTime)
{
this.BufferSize = bufferSize;
this.TryCount = tryCount;
this.SleepTime = sleepTime;
}
/// <summary>
/// 下载缓冲区大小
/// </summary>
public int BufferSize;
/// <summary>
/// 重试次数
/// </summary>
public int TryCount;
/// <summary>
/// 自动重下休眠时间, 毫秒
/// </summary>
public int SleepTime;
}
}