| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- using System;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Threading;
- namespace Masuit.Tools.Net
- {
- /// <summary>
- /// 部分下载器
- /// </summary>
- public class PartialDownloader
- {
- #region Variables
- /// <summary>
- /// 这部分完成事件
- /// </summary>
- public event EventHandler DownloadPartCompleted;
- /// <summary>
- /// 部分下载进度改变事件
- /// </summary>
- public event EventHandler DownloadPartProgressChanged;
- /// <summary>
- /// 部分下载停止事件
- /// </summary>
- public event EventHandler DownloadPartStopped;
- HttpWebRequest _req;
- HttpWebResponse _resp;
- Stream _tempStream;
- FileStream _file;
- private readonly AsyncOperation _aop = AsyncOperationManager.CreateOperation(null);
- private readonly Stopwatch _stp;
- readonly int[] _lastSpeeds;
- int _counter;
- bool _stop, _wait;
- #endregion
- #region PartialDownloader
- /// <summary>
- /// 部分块下载
- /// </summary>
- /// <param name="url"></param>
- /// <param name="dir"></param>
- /// <param name="fileGuid"></param>
- /// <param name="from"></param>
- /// <param name="to"></param>
- /// <param name="rangeAllowed"></param>
- public PartialDownloader(string url, string dir, string fileGuid, int from, int to, bool rangeAllowed)
- {
- _from = from;
- _to = to;
- _url = url;
- _rangeAllowed = rangeAllowed;
- _fileGuid = fileGuid;
- _directory = dir;
- _lastSpeeds = new int[10];
- _stp = new Stopwatch();
- }
- #endregion
- void DownloadProcedure()
- {
- _file = new FileStream(FullPath, FileMode.Create, FileAccess.ReadWrite);
- #region Request-Response
- _req = WebRequest.Create(_url) as HttpWebRequest;
- if (_req != null)
- {
- _req.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
- _req.AllowAutoRedirect = true;
- _req.MaximumAutomaticRedirections = 5;
- _req.ServicePoint.ConnectionLimit += 1;
- _req.ServicePoint.Expect100Continue = true;
- _req.ProtocolVersion = HttpVersion.Version10;
- ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.SystemDefault;
- ServicePointManager.Expect100Continue = true;
- if (_rangeAllowed)
- _req.AddRange(_from, _to);
- _resp = _req.GetResponse() as HttpWebResponse;
- #endregion
- #region Some Stuff
- if (_resp != null)
- {
- _contentLength = _resp.ContentLength;
- if (_contentLength <= 0 || (_rangeAllowed && _contentLength != _to - _from + 1))
- throw new Exception("Invalid response content");
- _tempStream = _resp.GetResponseStream();
- int bytesRead;
- byte[] buffer = new byte[4096];
- _stp.Start();
- #endregion
- #region Procedure Loop
- while (_tempStream != null && (bytesRead = _tempStream.Read(buffer, 0, buffer.Length)) > 0)
- {
- while (_wait)
- {
- }
- if (_totalBytesRead + bytesRead > _contentLength)
- bytesRead = (int)(_contentLength - _totalBytesRead);
- _file.Write(buffer, 0, bytesRead);
- _totalBytesRead += bytesRead;
- _lastSpeeds[_counter] = (int)(_totalBytesRead / Math.Ceiling(_stp.Elapsed.TotalSeconds));
- _counter = (_counter >= 9) ? 0 : _counter + 1;
- int tempProgress = (int)(_totalBytesRead * 100 / _contentLength);
- if (_progress != tempProgress)
- {
- _progress = tempProgress;
- _aop.Post(state =>
- {
- DownloadPartProgressChanged?.Invoke(this, EventArgs.Empty);
- }, null);
- }
- if (_stop || (_rangeAllowed && _totalBytesRead == _contentLength))
- {
- break;
- }
- }
- #endregion
- #region Close Resources
- _file.Close();
- _resp.Close();
- }
- _tempStream?.Close();
- _req.Abort();
- }
- _stp.Stop();
- #endregion
- #region Fire Events
- if (!_stop && DownloadPartCompleted != null)
- _aop.Post(state =>
- {
- _completed = true;
- DownloadPartCompleted(this, EventArgs.Empty);
- }, null);
- if (_stop && DownloadPartStopped != null)
- _aop.Post(state => DownloadPartStopped(this, EventArgs.Empty), null);
- #endregion
- }
- #region Public Methods
- /// <summary>
- /// 启动下载
- /// </summary>
- public void Start()
- {
- _stop = false;
- Thread procThread = new Thread(DownloadProcedure);
- procThread.Start();
- }
- /// <summary>
- /// 下载停止
- /// </summary>
- public void Stop()
- {
- _stop = true;
- }
- /// <summary>
- /// 暂停等待下载
- /// </summary>
- public void Wait()
- {
- _wait = true;
- }
- /// <summary>
- /// 稍后唤醒
- /// </summary>
- public void ResumeAfterWait()
- {
- _wait = false;
- }
- #endregion
- #region Property Variables
- private readonly int _from;
- private int _to;
- private readonly string _url;
- private readonly bool _rangeAllowed;
- private long _contentLength;
- private int _totalBytesRead;
- private readonly string _fileGuid;
- private readonly string _directory;
- private int _progress;
- private bool _completed;
- #endregion
- #region Properties
- /// <summary>
- /// 下载已停止
- /// </summary>
- public bool Stopped => _stop;
- /// <summary>
- /// 下载已完成
- /// </summary>
- public bool Completed => _completed;
- /// <summary>
- /// 下载进度
- /// </summary>
- public int Progress => _progress;
- /// <summary>
- /// 下载目录
- /// </summary>
- public string Directory => _directory;
- /// <summary>
- /// 文件名
- /// </summary>
- public string FileName => _fileGuid;
- /// <summary>
- /// 已读字节数
- /// </summary>
- public long TotalBytesRead => _totalBytesRead;
- /// <summary>
- /// 内容长度
- /// </summary>
- public long ContentLength => _contentLength;
- /// <summary>
- /// RangeAllowed
- /// </summary>
- public bool RangeAllowed => _rangeAllowed;
- /// <summary>
- /// url
- /// </summary>
- public string Url => _url;
- /// <summary>
- /// to
- /// </summary>
- public int To
- {
- get => _to;
- set
- {
- _to = value;
- _contentLength = _to - _from + 1;
- }
- }
- /// <summary>
- /// from
- /// </summary>
- public int From => _from;
- /// <summary>
- /// 当前位置
- /// </summary>
- public int CurrentPosition => _from + _totalBytesRead - 1;
- /// <summary>
- /// 剩余字节数
- /// </summary>
- public int RemainingBytes => (int)(_contentLength - _totalBytesRead);
- /// <summary>
- /// 完整路径
- /// </summary>
- public string FullPath => Path.Combine(_directory, _fileGuid);
- /// <summary>
- /// 下载速度
- /// </summary>
- public int SpeedInBytes
- {
- get
- {
- if (_completed)
- return 0;
- int totalSpeeds = _lastSpeeds.Sum();
- return totalSpeeds / 10;
- }
- }
- #endregion
- }
- }
|