using System; using System.IO; using System.Net; using System.Net.Http; using System.Threading; namespace QYMessageCenter.Common.Http { /// /// Http下载器 /// 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 /// /// 下载进度变化参数 /// private DownloadProgressChangedEventArgs changeArgs; /// /// 下载完成参数 /// private DownloadCompletedEventArgs complateArgs; /// /// 下载参数配置类 /// private DownloadSetting downloadSetting; /// /// 下载缓冲区 /// 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 /// /// 下载进度变化事件 /// public event DownloadProgressChangedEventHandler OnDownloadProgressChanged; /// /// 下载完成事件 /// public event DownloadCompletedEventHandler OnDownloadComplated; /// /// 自动重下事件 /// public event ReDownloadEventHandler OnReDownload; #endregion #region Method /// /// 设置下载参数 /// /// 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; } /// /// 获取文件总大小 /// /// /// 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; } } /// /// 取消/暂停下载 /// public void CancelDownload() { this.changeArgs.Cancel = true; this.complateArgs.Cancelled = true; } #endregion } /// /// 下载进度变化参数 /// public class DownloadProgressChangedEventArgs { public DownloadProgressChangedEventArgs() { ProgressPercentage = 0.0; Cancel = false; } /// /// 下载进度百分比 /// public double ProgressPercentage; private long currentSize; /// /// 当前下载总大小 /// public long CurrentSize { get { return currentSize; } set { currentSize = value; this.ProgressPercentage = Math.Round((CurrentSize * 1.0 / TotalSize) * 100, 2); } } /// /// 文件总大小 /// public long TotalSize; /// /// 取消状态 /// public bool Cancel; } /// /// 下载完成参数 /// public class DownloadCompletedEventArgs { public DownloadCompletedEventArgs() { Cancelled = false; Error = null; } /// /// 下载异常 /// public Exception Error; /// /// 重试次数 /// public int TryCount; /// /// 下载操作是否被取消 【取消则为true;默认false】 /// public bool Cancelled; } public class DownloadSetting { public DownloadSetting() { this.BufferSize = 1024 * 1024 * 1; this.TryCount = 5; this.SleepTime = 5000; } /// /// /// /// /// /// 毫秒 public DownloadSetting(int bufferSize, int tryCount, int sleepTime) { this.BufferSize = bufferSize; this.TryCount = tryCount; this.SleepTime = sleepTime; } /// /// 下载缓冲区大小 /// public int BufferSize; /// /// 重试次数 /// public int TryCount; /// /// 自动重下休眠时间, 毫秒 /// public int SleepTime; } }