using System; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Threading; namespace Masuit.Tools.Net { /// /// 部分下载器 /// public class PartialDownloader { #region Variables /// /// 这部分完成事件 /// public event EventHandler DownloadPartCompleted; /// /// 部分下载进度改变事件 /// public event EventHandler DownloadPartProgressChanged; /// /// 部分下载停止事件 /// 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 /// /// 部分块下载 /// /// /// /// /// /// /// 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 /// /// 启动下载 /// public void Start() { _stop = false; Thread procThread = new Thread(DownloadProcedure); procThread.Start(); } /// /// 下载停止 /// public void Stop() { _stop = true; } /// /// 暂停等待下载 /// public void Wait() { _wait = true; } /// /// 稍后唤醒 /// 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 /// /// 下载已停止 /// public bool Stopped => _stop; /// /// 下载已完成 /// public bool Completed => _completed; /// /// 下载进度 /// public int Progress => _progress; /// /// 下载目录 /// public string Directory => _directory; /// /// 文件名 /// public string FileName => _fileGuid; /// /// 已读字节数 /// public long TotalBytesRead => _totalBytesRead; /// /// 内容长度 /// public long ContentLength => _contentLength; /// /// RangeAllowed /// public bool RangeAllowed => _rangeAllowed; /// /// url /// public string Url => _url; /// /// to /// public int To { get => _to; set { _to = value; _contentLength = _to - _from + 1; } } /// /// from /// public int From => _from; /// /// 当前位置 /// public int CurrentPosition => _from + _totalBytesRead - 1; /// /// 剩余字节数 /// public int RemainingBytes => (int)(_contentLength - _totalBytesRead); /// /// 完整路径 /// public string FullPath => Path.Combine(_directory, _fileGuid); /// /// 下载速度 /// public int SpeedInBytes { get { if (_completed) return 0; int totalSpeeds = _lastSpeeds.Sum(); return totalSpeeds / 10; } } #endregion } }