修正多线程下载的bug

croire 2 years ago
parent 8958cf46c4
commit ade4c78190

@ -27,7 +27,8 @@ namespace DownKyi.Core.Downloader
private bool _rangeAllowed; private bool _rangeAllowed;
private readonly HttpWebRequest _request; private readonly HttpWebRequest _request;
private Action<HttpWebRequest> _requestConfigure = req => req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36"; private Action<HttpWebRequest> _requestConfigure = req => req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36";
#endregion
#endregion 属性
#region 公共属性 #region 公共属性
@ -67,12 +68,14 @@ namespace DownKyi.Core.Downloader
get get
{ {
try try
{
lock (this)
{ {
return PartialDownloaderList.Where(t => t != null).Sum(t => t.TotalBytesRead); return PartialDownloaderList.Where(t => t != null).Sum(t => t.TotalBytesRead);
} }
catch (Exception e) }
catch
{ {
Logging.LogManager.Error(e);
return 0; return 0;
} }
} }
@ -91,7 +94,16 @@ namespace DownKyi.Core.Downloader
/// <summary> /// <summary>
/// 下载速度 /// 下载速度
/// </summary> /// </summary>
public float TotalSpeedInBytes => PartialDownloaderList.Sum(t => t.SpeedInBytes); public float TotalSpeedInBytes
{
get
{
lock (this)
{
return PartialDownloaderList.Sum(t => t.SpeedInBytes);
}
}
}
/// <summary> /// <summary>
/// 下载块 /// 下载块
@ -103,7 +115,7 @@ namespace DownKyi.Core.Downloader
/// </summary> /// </summary>
public string FilePath { get; set; } public string FilePath { get; set; }
#endregion #endregion 公共属性
#region 变量 #region 变量
@ -124,7 +136,7 @@ namespace DownKyi.Core.Downloader
private readonly AsyncOperation _aop; private readonly AsyncOperation _aop;
#endregion #endregion 变量
#region 下载管理器 #region 下载管理器
@ -166,7 +178,7 @@ namespace DownKyi.Core.Downloader
{ {
} }
#endregion #endregion 下载管理器
#region 事件 #region 事件
@ -181,16 +193,16 @@ namespace DownKyi.Core.Downloader
return; return;
} }
PartialDownloaderList.Sort((x, y) => y.RemainingBytes - x.RemainingBytes); PartialDownloaderList.Sort((x, y) => (int)(y.RemainingBytes - x.RemainingBytes));
int rem = PartialDownloaderList[0].RemainingBytes; var rem = PartialDownloaderList[0].RemainingBytes;
if (rem < 50 * 1024) if (rem < 50 * 1024)
{ {
WaitOrResumeAll(PartialDownloaderList, false); WaitOrResumeAll(PartialDownloaderList, false);
return; return;
} }
int from = PartialDownloaderList[0].CurrentPosition + rem / 2; var from = PartialDownloaderList[0].CurrentPosition + rem / 2;
int to = PartialDownloaderList[0].To; var to = PartialDownloaderList[0].To;
if (from > to) if (from > to)
{ {
WaitOrResumeAll(PartialDownloaderList, false); WaitOrResumeAll(PartialDownloaderList, false);
@ -202,16 +214,19 @@ namespace DownKyi.Core.Downloader
var temp = new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString(), from, to, true); var temp = new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString(), from, to, true);
temp.DownloadPartCompleted += temp_DownloadPartCompleted; temp.DownloadPartCompleted += temp_DownloadPartCompleted;
temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged; temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged;
lock (this)
{
PartialDownloaderList.Add(temp); PartialDownloaderList.Add(temp);
}
temp.Start(_requestConfigure); temp.Start(_requestConfigure);
} }
void temp_DownloadPartProgressChanged(object sender, EventArgs e) private void temp_DownloadPartProgressChanged(object sender, EventArgs e)
{ {
UpdateProgress(); UpdateProgress();
} }
void UpdateProgress() private void UpdateProgress()
{ {
int pr = (int)(TotalBytesReceived * 1d / Size * 100); int pr = (int)(TotalBytesReceived * 1d / Size * 100);
if (TotalProgress != pr) if (TotalProgress != pr)
@ -224,11 +239,11 @@ namespace DownKyi.Core.Downloader
} }
} }
#endregion #endregion 事件
#region 方法 #region 方法
void CreateFirstPartitions() private void CreateFirstPartitions()
{ {
Size = GetContentLength(ref _rangeAllowed, ref _url); Size = GetContentLength(ref _rangeAllowed, ref _url);
int maximumPart = (int)(Size / (25 * 1024)); int maximumPart = (int)(Size / (25 * 1024));
@ -247,17 +262,21 @@ namespace DownKyi.Core.Downloader
var temp = CreateNew(i, NumberOfParts, Size); var temp = CreateNew(i, NumberOfParts, Size);
temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged; temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged;
temp.DownloadPartCompleted += temp_DownloadPartCompleted; temp.DownloadPartCompleted += temp_DownloadPartCompleted;
lock (this)
{
PartialDownloaderList.Add(temp); PartialDownloaderList.Add(temp);
}
temp.Start(_requestConfigure); temp.Start(_requestConfigure);
} }
} }
void MergeParts() private void MergeParts()
{ {
var mergeOrderedList = PartialDownloaderList.OrderBy(x => x.From); var mergeOrderedList = PartialDownloaderList.OrderBy(x => x.From);
var dir = new FileInfo(FilePath).DirectoryName; var dir = new FileInfo(FilePath).DirectoryName;
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);
using (var fs = File.OpenWrite(FilePath)) using (var fs = File.OpenWrite(FilePath))
{ {
long totalBytesWrite = 0; long totalBytesWrite = 0;
@ -298,12 +317,12 @@ namespace DownKyi.Core.Downloader
} }
} }
PartialDownloader CreateNew(int order, int parts, long contentLength) private PartialDownloader CreateNew(int order, int parts, long contentLength)
{ {
int division = (int)contentLength / parts; var division = contentLength / parts;
int remaining = (int)contentLength % parts; var remaining = contentLength % parts;
int start = division * order; var start = division * order;
int end = start + division - 1; var end = start + division - 1;
end += order == parts - 1 ? remaining : 0; end += order == parts - 1 ? remaining : 0;
return new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString("N"), start, end, true); return new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString("N"), start, end, true);
} }
@ -315,15 +334,15 @@ namespace DownKyi.Core.Downloader
/// <param name="wait"></param> /// <param name="wait"></param>
public static void WaitOrResumeAll(List<PartialDownloader> list, bool wait) public static void WaitOrResumeAll(List<PartialDownloader> list, bool wait)
{ {
foreach (var item in list) for (var index = 0; index < list.Count; index++)
{ {
if (wait) if (wait)
{ {
item.Wait(); list[index].Wait();
} }
else else
{ {
item.ResumeAfterWait(); list[index].ResumeAfterWait();
} }
} }
} }
@ -349,23 +368,21 @@ namespace DownKyi.Core.Downloader
_request.ServicePoint.ConnectionLimit = 4; _request.ServicePoint.ConnectionLimit = 4;
_requestConfigure(_request); _requestConfigure(_request);
long ctl;
using (var resp = _request.GetResponse() as HttpWebResponse) using (var resp = _request.GetResponse() as HttpWebResponse)
{ {
redirectedUrl = resp.ResponseUri.OriginalString; redirectedUrl = resp.ResponseUri.OriginalString;
ctl = resp.ContentLength; var ctl = resp.ContentLength;
rangeAllowed = resp.Headers.AllKeys.Select((v, i) => new rangeAllowed = resp.Headers.AllKeys.Select((v, i) => new
{ {
HeaderName = v, HeaderName = v,
HeaderValue = resp.Headers[i] HeaderValue = resp.Headers[i]
}).Any(k => k.HeaderName.ToLower().Contains("range") && k.HeaderValue.ToLower().Contains("byte")); }).Any(k => k.HeaderName.ToLower().Contains("range") && k.HeaderValue.ToLower().Contains("byte"));
_request.Abort(); _request.Abort();
}
return ctl; return ctl;
} }
}
#endregion #endregion 方法
#region 公共方法 #region 公共方法
@ -373,46 +390,25 @@ namespace DownKyi.Core.Downloader
/// 暂停下载 /// 暂停下载
/// </summary> /// </summary>
public void Pause() public void Pause()
{
lock (this)
{ {
foreach (var t in PartialDownloaderList.Where(t => !t.Completed)) foreach (var t in PartialDownloaderList.Where(t => !t.Completed))
{ {
t.Stop(); t.Stop();
} }
Thread.Sleep(200);
} }
/// <summary> Thread.Sleep(200);
/// 开始下载
/// </summary>
public void StartAsync()
{
//Task th = new Task(CreateFirstPartitions);
//th.Start();
StartAsync(false);
} }
/// <summary> /// <summary>
/// 开始下载 /// 开始下载
/// </summary> /// </summary>
/// <param name="isWait"></param> public void Start()
public void StartAsync(bool isWait)
{ {
Task th = new Task(CreateFirstPartitions); Task th = new Task(CreateFirstPartitions);
th.Start(); th.Start();
if (isWait)
{
th.Wait();
}
}
/// <summary>
/// 开始下载
/// </summary>
public void Start()
{
CreateFirstPartitions();
} }
/// <summary> /// <summary>
@ -425,8 +421,8 @@ namespace DownKyi.Core.Downloader
{ {
if (PartialDownloaderList[i].Stopped) if (PartialDownloaderList[i].Stopped)
{ {
int from = PartialDownloaderList[i].CurrentPosition + 1; var from = PartialDownloaderList[i].CurrentPosition + 1;
int to = PartialDownloaderList[i].To; var to = PartialDownloaderList[i].To;
if (from > to) if (from > to)
{ {
continue; continue;
@ -435,13 +431,16 @@ namespace DownKyi.Core.Downloader
var temp = new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString(), from, to, _rangeAllowed); var temp = new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString(), from, to, _rangeAllowed);
temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged; temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged;
temp.DownloadPartCompleted += temp_DownloadPartCompleted; temp.DownloadPartCompleted += temp_DownloadPartCompleted;
lock (this)
{
PartialDownloaderList.Add(temp); PartialDownloaderList.Add(temp);
}
PartialDownloaderList[i].To = PartialDownloaderList[i].CurrentPosition; PartialDownloaderList[i].To = PartialDownloaderList[i].CurrentPosition;
temp.Start(_requestConfigure); temp.Start(_requestConfigure);
} }
} }
} }
#endregion #endregion 公共方法
} }
} }

@ -29,11 +29,11 @@ namespace DownKyi.Core.Downloader
public event EventHandler DownloadPartStopped; public event EventHandler DownloadPartStopped;
private readonly AsyncOperation _aop = AsyncOperationManager.CreateOperation(null); private readonly AsyncOperation _aop = AsyncOperationManager.CreateOperation(null);
readonly int[] _lastSpeeds; private readonly int[] _lastSpeeds;
int _counter; private long _counter;
private long _to;
private long _totalBytesRead;
private bool _wait; private bool _wait;
private int _to;
private int _totalBytesRead;
/// <summary> /// <summary>
/// 下载已停止 /// 下载已停止
@ -83,7 +83,7 @@ namespace DownKyi.Core.Downloader
/// <summary> /// <summary>
/// to /// to
/// </summary> /// </summary>
public int To public long To
{ {
get => _to; get => _to;
set set
@ -96,17 +96,17 @@ namespace DownKyi.Core.Downloader
/// <summary> /// <summary>
/// from /// from
/// </summary> /// </summary>
public int From { get; } public long From { get; }
/// <summary> /// <summary>
/// 当前位置 /// 当前位置
/// </summary> /// </summary>
public int CurrentPosition => From + _totalBytesRead - 1; public long CurrentPosition => From + _totalBytesRead - 1;
/// <summary> /// <summary>
/// 剩余字节数 /// 剩余字节数
/// </summary> /// </summary>
public int RemainingBytes => (int)(ContentLength - _totalBytesRead); public long RemainingBytes => ContentLength - _totalBytesRead;
/// <summary> /// <summary>
/// 完整路径 /// 完整路径
@ -139,7 +139,7 @@ namespace DownKyi.Core.Downloader
/// <param name="from"></param> /// <param name="from"></param>
/// <param name="to"></param> /// <param name="to"></param>
/// <param name="rangeAllowed"></param> /// <param name="rangeAllowed"></param>
public PartialDownloader(string url, string dir, string fileGuid, int from, int to, bool rangeAllowed) public PartialDownloader(string url, string dir, string fileGuid, long from, long to, bool rangeAllowed)
{ {
From = from; From = from;
_to = to; _to = to;
@ -150,7 +150,7 @@ namespace DownKyi.Core.Downloader
_lastSpeeds = new int[10]; _lastSpeeds = new int[10];
} }
void DownloadProcedure(Action<HttpWebRequest> config) private void DownloadProcedure(Action<HttpWebRequest> config)
{ {
using (var file = new FileStream(FullPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete)) using (var file = new FileStream(FullPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete))
{ {
@ -163,6 +163,7 @@ namespace DownKyi.Core.Downloader
req.ServicePoint.ConnectionLimit += 1; req.ServicePoint.ConnectionLimit += 1;
req.ServicePoint.Expect100Continue = true; req.ServicePoint.Expect100Continue = true;
req.ProtocolVersion = HttpVersion.Version11; req.ProtocolVersion = HttpVersion.Version11;
req.Proxy = WebRequest.GetSystemWebProxy();
config(req); config(req);
if (RangeAllowed) if (RangeAllowed)
{ {

@ -15,7 +15,7 @@ namespace DownKyi.Core.Logging
/// </summary> /// </summary>
public class LogManager public class LogManager
{ {
static readonly ConcurrentQueue<Tuple<string, string>> LogQueue = new ConcurrentQueue<Tuple<string, string>>(); private static readonly ConcurrentQueue<Tuple<string, string>> LogQueue = new ConcurrentQueue<Tuple<string, string>>();
/// <summary> /// <summary>
/// 自定义事件 /// 自定义事件
@ -63,14 +63,21 @@ namespace DownKyi.Core.Logging
private static AutoResetEvent Pause => new AutoResetEvent(false); private static AutoResetEvent Pause => new AutoResetEvent(false);
private static string logDirectory;
/// <summary> /// <summary>
/// 日志存放目录默认日志放在当前应用程序运行目录下的logs文件夹中 /// 日志存放目录默认日志放在当前应用程序运行目录下的logs文件夹中
/// </summary> /// </summary>
public static string LogDirectory public static string LogDirectory
{ {
get => Storage.Constant.Logs; // Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"); get => logDirectory ?? (Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory).Any(s => s.Contains("Web.config")) ? AppDomain.CurrentDomain.BaseDirectory + @"App_Data\Logs\" : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"));
set set
{ {
//自定义目录
if (!Directory.Exists(value))
{
Directory.CreateDirectory(value);
}
logDirectory = value;
} }
} }

@ -356,7 +356,7 @@ namespace DownKyi.Services.Download
}; };
// 开始下载 // 开始下载
mtd.StartAsync(); mtd.Start();
// 阻塞当前任务,监听暂停事件 // 阻塞当前任务,监听暂停事件
while (!isComplete) while (!isComplete)

Loading…
Cancel
Save