Skip to content

Commit

Permalink
添加新的实验性下载器
Browse files Browse the repository at this point in the history
  • Loading branch information
YangSpring114 committed Dec 7, 2024
1 parent a9ab3fa commit 330cc40
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 159 deletions.
166 changes: 50 additions & 116 deletions MinecraftLaunch.Test/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,56 @@
using MinecraftLaunch.Components.Checker;
using System.Net;
using DownloadProgressChangedEventArgs = MinecraftLaunch.Classes.Models.Event.DownloadProgressChangedEventArgs;
using MinecraftLaunch.Components.Downloader;

IDownloader downloader = new FileDownloader(1024 * 1024, 8, 256);

GameResolver gameResolve = new("C:\\Users\\wxysd\\Desktop\\temp\\.minecraft");
ResourceChecker resourceChecker = new(gameResolve.GetGameEntity("1.21.1"));
await resourceChecker.CheckAsync();

int count = 0;
double speed = 0;
var req = new GroupDownloadRequest(resourceChecker.MissingResources.Select(x => x.ToDownloadRequest()));
req.DownloadSpeedChanged += s => {
speed = s;
};

var str = string.Empty;

req.SingleRequestCompleted += (a, arg) => {
count++;
str = $"{count}/{resourceChecker.MissingResources.Count} - {((double)count / (double)resourceChecker.MissingResources.Count) * 100:0.00}% - {GetSpeedText(speed)} - {a.FileInfo.Name} - {arg.Type}";
};

System.Timers.Timer timer = new(TimeSpan.FromSeconds(1));
timer.Elapsed += (_, _) => {
Console.WriteLine(str);
};

timer.Start();
var res = await downloader.DownloadFilesAsync(req);
timer.Stop();

Console.WriteLine(res.Type);

string GetSpeedText(double speed) {
if (speed < 1024.0) {
return speed.ToString("0") + " B/s";
}

if (speed < 1024.0 * 1024.0) {
return (speed / 1024.0).ToString("0.0") + " KB/s";
}

if (speed < 1024.0 * 1024.0 * 1024.0) {
return (speed / (1024.0 * 1024.0)).ToString("0.00") + " MB/s";
}

return "0";
}

return;

//RD rd = new();
//rd.Completed += OnCompleted;
Expand Down Expand Up @@ -241,119 +290,4 @@ void VanlliaInstaller_ProgressChanged(object? sender, ProgressChangedEventArgs e

#endregion

Console.ReadKey();

static string GetSpeedText(double speed) {
if (speed < 1024.0) {
return speed.ToString("0") + " B/s";
}

if (speed < 1024.0 * 1024.0) {
return (speed / 1024.0).ToString("0.0") + " KB/s";
}

if (speed < 1024.0 * 1024.0 * 1024.0) {
return (speed / (1024.0 * 1024.0)).ToString("0.00") + " MB/s";
}

return "0";
}

class RD {
private const int BUFFER_SIZE = 4096;
private const double UPDATE_INTERVAL = 1.0;

private readonly MemoryPool<byte> _bufferPool = MemoryPool<byte>.Shared;
private readonly System.Timers.Timer _timer = new(TimeSpan.FromSeconds(UPDATE_INTERVAL));

private int _totalCount;
private int _failedCount;
private int _completedCount;

private int _totalBytes;
private int _downloadedBytes;
private int _previousDownloadedBytes;

public event EventHandler<EventArgs> Completed;
public event EventHandler<DownloadProgressChangedEventArgs> ProgressChanged;

public async Task DownloadAsync(IEnumerable<IDownloadEntry> downloadEntries, CancellationTokenSource tokenSource = default) {
_totalCount = downloadEntries.Count();
_totalBytes = downloadEntries.Sum(item => item.Size);
_timer.Elapsed += (_, _) => UpdateDownloadProgress();
_timer.Start();

var actionBlock = new ActionBlock<IDownloadEntry>(async x => {
if (!await DownloadFileAsync(x)) {
Interlocked.Increment(ref _failedCount);
}

}, new ExecutionDataflowBlockOptions {
MaxDegreeOfParallelism = 256,
CancellationToken = tokenSource is null ? default : tokenSource.Token
});

foreach (var downloadEntry in downloadEntries) {
actionBlock.Post(downloadEntry);
}

actionBlock.Complete();
await actionBlock.Completion;

_timer.Stop();
UpdateDownloadProgress();
Completed?.Invoke(this, EventArgs.Empty);
}

private void UpdateDownloadProgress() {
int diffBytes = _downloadedBytes - _previousDownloadedBytes;
_previousDownloadedBytes = _downloadedBytes;

var progress = new DownloadProgressChangedEventArgs {
TotalCount = _totalCount,
TotalBytes = _totalBytes,
FailedCount = _failedCount,
CompletedCount = _completedCount,
DownloadedBytes = _downloadedBytes,
Speed = diffBytes / UPDATE_INTERVAL
};

ProgressChanged?.Invoke(this, progress);
}

private async Task<bool> DownloadFileAsync(IDownloadEntry entry) {
int bytesRead;
var buffer = _bufferPool.Rent(BUFFER_SIZE);

try {
using var flurlResponse = await entry.Url.GetAsync();

if (flurlResponse.StatusCode is 302) {
entry.Url = flurlResponse.ResponseMessage.Headers.Location!.AbsoluteUri;
return await DownloadFileAsync(entry);
}

if (Path.IsPathRooted(entry.Path)) {
Directory.CreateDirectory(Path.GetDirectoryName(entry.Path));
}

using var responseStream = await flurlResponse.GetStreamAsync();
using var fileStream = new FileStream(entry.Path, FileMode.Create, FileAccess.Write, FileShare.None);

if (entry.Size is 0) {
entry.Size = Convert.ToInt32(flurlResponse.ResponseMessage.Content.Headers.ContentLength);
Interlocked.Add(ref _totalBytes, entry.Size);
}

while ((bytesRead = await responseStream.ReadAsync(buffer.Memory)) > 0) {
await fileStream.WriteAsync(buffer.Memory[..bytesRead]);
Interlocked.Add(ref _downloadedBytes, bytesRead);
}

Interlocked.Increment(ref _completedCount);
return true;
} catch (Exception) {
return false;
}
}
}
Console.ReadKey();
36 changes: 18 additions & 18 deletions MinecraftLaunch.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinecraftLaunch.Simple", "M
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MinecraftLaunch.Banchmark", "MinecraftLaunch.BanchTest\MinecraftLaunch.Banchmark.csproj", "{95AA9E60-2FD0-41C1-ACC7-1C3021B30F73}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GBCLV3", "..\..\..\Code\GBCLV3\GBCLV3\GBCLV3.csproj", "{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Natsurainko.FluentLauncher", "..\..\..\Code\Natsurainko.FluentLauncher\Natsurainko.FluentLauncher\Natsurainko.FluentLauncher.csproj", "{F4033B16-7B0A-480C-9AE0-2F24893225C1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Natsurainko.FluentCore", "..\..\..\Code\Natsurainko.FluentLauncher\Natsurainko.FluentCore\Natsurainko.FluentCore\Natsurainko.FluentCore.csproj", "{3C661AFF-D7B5-494A-B4DE-374027507CF5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -73,22 +73,6 @@ Global
{95AA9E60-2FD0-41C1-ACC7-1C3021B30F73}.Release|x64.Build.0 = Release|Any CPU
{95AA9E60-2FD0-41C1-ACC7-1C3021B30F73}.Release|x86.ActiveCfg = Release|Any CPU
{95AA9E60-2FD0-41C1-ACC7-1C3021B30F73}.Release|x86.Build.0 = Release|Any CPU
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|Any CPU.ActiveCfg = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|Any CPU.Build.0 = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|ARM64.ActiveCfg = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|ARM64.Build.0 = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|x64.ActiveCfg = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|x64.Build.0 = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|x86.ActiveCfg = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Debug|x86.Build.0 = Debug|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|Any CPU.ActiveCfg = Release|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|Any CPU.Build.0 = Release|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|ARM64.ActiveCfg = Release|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|ARM64.Build.0 = Release|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|x64.ActiveCfg = Release|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|x64.Build.0 = Release|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|x86.ActiveCfg = Release|x64
{30B58822-FDF3-4C4C-8FE7-B7A95C5CCCC8}.Release|x86.Build.0 = Release|x64
{F4033B16-7B0A-480C-9AE0-2F24893225C1}.Debug|Any CPU.ActiveCfg = Debug|x64
{F4033B16-7B0A-480C-9AE0-2F24893225C1}.Debug|Any CPU.Build.0 = Debug|x64
{F4033B16-7B0A-480C-9AE0-2F24893225C1}.Debug|Any CPU.Deploy.0 = Debug|x64
Expand All @@ -113,6 +97,22 @@ Global
{F4033B16-7B0A-480C-9AE0-2F24893225C1}.Release|x86.ActiveCfg = Release|x86
{F4033B16-7B0A-480C-9AE0-2F24893225C1}.Release|x86.Build.0 = Release|x86
{F4033B16-7B0A-480C-9AE0-2F24893225C1}.Release|x86.Deploy.0 = Release|x86
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|ARM64.Build.0 = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|x64.ActiveCfg = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|x64.Build.0 = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|x86.ActiveCfg = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Debug|x86.Build.0 = Debug|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|Any CPU.Build.0 = Release|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|ARM64.ActiveCfg = Release|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|ARM64.Build.0 = Release|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|x64.ActiveCfg = Release|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|x64.Build.0 = Release|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|x86.ActiveCfg = Release|Any CPU
{3C661AFF-D7B5-494A-B4DE-374027507CF5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
7 changes: 7 additions & 0 deletions MinecraftLaunch/Classes/Enums/DownloadResultType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MinecraftLaunch.Classes.Enums;

public enum DownloadResultType {
Successful,
Cancelled,
Failed
}
15 changes: 12 additions & 3 deletions MinecraftLaunch/Classes/Interfaces/IDownloader.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
namespace MinecraftLaunch.Classes.Interfaces;
using MinecraftLaunch.Classes.Models.Download;

namespace MinecraftLaunch.Classes.Interfaces;

/// <summary>
/// 下载器统一接口
/// </summary>
public interface IDownloader {
/// <summary>
/// 单个文件下载
/// </summary>
Task<DownloadResult> DownloadFileAsync(DownloadRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// 异步下载方法
/// 多个文件下载
/// </summary>
ValueTask<bool> DownloadAsync();
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<GroupDownloadResult> DownloadFilesAsync(GroupDownloadRequest request, CancellationToken cancellationToken = default);
}
40 changes: 24 additions & 16 deletions MinecraftLaunch/Classes/Models/Download/DownloadRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,42 @@
/// <summary>
/// 下载请求信息配置记录类
/// </summary>
public sealed record DownloadRequest {
public record struct DownloadRequest {
/// <summary>
/// 大文件判断阈值
/// </summary>
public long FileSizeThreshold { get; set; }

/// <summary>
/// 分片数量
/// </summary>
public int MultiPartsCount { get; set; }

/// <summary>
/// 最大并行下载线程数
/// 下载链接
/// </summary>
public int MultiThreadsCount { get; set; }
public string Url { get; set; }

/// <summary>
/// 下载链接
/// 文件大小
/// </summary>
public string Url { get; set; }
public long Size { get; set; }

/// <summary>
/// 保存文件信息
/// </summary>
public FileInfo FileInfo { get; set; }

/// <summary>
/// 是否启用大文件分片下载
/// 下载进度接收事件(单位:Byte)
/// </summary>
public Action<long> BytesDownloaded { get; set; }

public int MultiPartsCount { get; set; }
public int MultiThreadsCount { get; set; }
public long FileSizeThreshold { get; set; }
public bool IsPartialContentSupported { get; set; }
}

public class GroupDownloadRequest {
public DateTime StartTime { get; init; }

public IEnumerable<DownloadRequest> Files { get; set; }
public Action<double> DownloadSpeedChanged { get; set; }
public Action<DownloadRequest, DownloadResult> SingleRequestCompleted { get; set; }

public GroupDownloadRequest(IEnumerable<DownloadRequest> files) {
Files = files;
StartTime = DateTime.Now;
}
}
19 changes: 19 additions & 0 deletions MinecraftLaunch/Classes/Models/Download/DownloadResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MinecraftLaunch.Classes.Enums;
using System.Collections.Frozen;

namespace MinecraftLaunch.Classes.Models.Download;

public record DownloadResult {
public DownloadResultType Type { get; init; }

public Exception? Exception { get; init; }

public DownloadResult(DownloadResultType type) {
Type = type;
}
}

public record GroupDownloadResult {
public required DownloadResultType Type { get; init; }
public required FrozenDictionary<DownloadRequest, DownloadResult> Failed { get; init; }
}
Loading

0 comments on commit 330cc40

Please sign in to comment.