下载修改

dev2.0
陈伟 2025-04-12 13:23:12 +08:00
parent d5ad1ebf9f
commit cfe2ccdf85
3 changed files with 74 additions and 143 deletions

View File

@ -1,7 +1,9 @@
using System.Windows; using System.Configuration;
using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using HeBianGu.Base.WpfBase; using HeBianGu.Base.WpfBase;
using Hopetry.Models; using Hopetry.Models;
using Hopetry.Provider;
using SqlSugar; using SqlSugar;
namespace Hopetry.Services; namespace Hopetry.Services;
@ -30,7 +32,8 @@ public class MinioDownloadTask : INotifyPropertyChanged
private ManualResetEventSlim _pauseEvent = new(true); private ManualResetEventSlim _pauseEvent = new(true);
// 任务属性绑定到UI // 任务属性绑定到UI
[SugarColumn(ColumnName = "task_id")] public int TaskId { get; set; } [SugarColumn(ColumnName = "task_id", IsPrimaryKey = true, IsIdentity = true)]
public int TaskId { get; set; }
[SugarColumn(ColumnName = "file_name")] [SugarColumn(ColumnName = "file_name")]
public string FileName { get; set; } public string FileName { get; set; }
@ -50,31 +53,30 @@ public class MinioDownloadTask : INotifyPropertyChanged
[SugarColumn(ColumnName = "file_path")] [SugarColumn(ColumnName = "file_path")]
public string FilePath { get; set; } public string FilePath { get; set; }
[SugarColumn(ColumnName = "status")] [SugarColumn(ColumnName = "status")] public string Status { get; private set; } = "等待中";
public string Status { get; private set; } = "等待中";
[SugarColumn(IsIgnore = true)]
public string Progress public string Progress
{ {
get { return TotalSize == 0 ? "0%" : $"{(Downloaded * 100 / TotalSize):0.0}%"; } get { return TotalSize == 0 ? "0%" : $"{(Downloaded * 100 / TotalSize):0.0}%"; }
} }
// 命令 [SugarColumn(IsIgnore = true)] public ICommand PauseCommand { get; }
public ICommand PauseCommand { get; } [SugarColumn(IsIgnore = true)] public ICommand CancelCommand { get; }
public ICommand CancelCommand { get; }
public MinioDownloadTask() public MinioDownloadTask()
{ {
PauseCommand = new CustomCommand(OnPause); PauseCommand = new CustomCommand(OnPause);
CancelCommand = new CustomCommand(OnCancel); CancelCommand = new CustomCommand(OnCancel);
} }
public MinioDownloadTask(MinioService minio, string bucket, string objectKey) public MinioDownloadTask(MinioService minio, string bucket, string objectKey)
{ {
_minio = minio; _minio = minio;
Bucket = bucket; Bucket = bucket;
ObjectKey = objectKey; ObjectKey = objectKey;
TaskId = Interlocked.Increment(ref _globalId);
FileName = Path.GetFileName(objectKey); FileName = Path.GetFileName(objectKey);
FilePath = "f:\\dest";
PauseCommand = new CustomCommand(OnPause); PauseCommand = new CustomCommand(OnPause);
CancelCommand = new CustomCommand(OnCancel); CancelCommand = new CustomCommand(OnCancel);
} }
@ -83,30 +85,35 @@ public class MinioDownloadTask : INotifyPropertyChanged
/// 下载 /// 下载
/// </summary> /// </summary>
/// <param name="savePath"></param> /// <param name="savePath"></param>
public async Task StartDownload(string savePath) public async Task StartDownload()
{ {
Status = "初始化";
_cts = new CancellationTokenSource(); _cts = new CancellationTokenSource();
var tmpPath = $"{savePath}.download";
try try
{ {
// 断点续传初始化 Status = "下载中";
if (File.Exists(tmpPath)) var updateTask = new MinioDownloadTask
Downloaded = new FileInfo(tmpPath).Length;
// 获取文件信息
var stat = await _minio.GetObjectMetadata(Bucket, ObjectKey);
TotalSize = stat.Size;
if (Downloaded >= TotalSize)
{ {
Status = "已完成"; TaskId = TaskId,
return; Status = Status
};
using (var client = SqlSugarConfig.GetSqlSugarScope())
{
await client.Updateable(updateTask).IgnoreNullColumns().ExecuteCommandAsync();
} }
// todo 下载逻辑 Console.WriteLine($"{ObjectKey}文件下载中...");
updateTask.Status = "下载中";
await _minio.DownLoadObject(Bucket, ObjectKey, FilePath, "");
Status = "已完成"; Status = "已完成";
updateTask.Status = Status;
using (var client = SqlSugarConfig.GetSqlSugarScope())
{
await client.Updateable(updateTask).IgnoreNullColumns().ExecuteCommandAsync();
}
Console.WriteLine($"文件{ObjectKey}下载完成");
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -150,6 +157,4 @@ public class MinioDownloadTask : INotifyPropertyChanged
protected void OnPropertyChanged(string propertyName) protected void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
private static int _globalId;
} }

View File

@ -16,6 +16,7 @@
<Button Grid.Column="0" <Button Grid.Column="0"
Margin="10,0" Margin="10,0"
Content="全部暂停" Content="全部暂停"
Command="{Binding DownViewModel.AddTaskCommand}"
Style="{DynamicResource {x:Static h:ButtonKeys.BorderBrushTransparent}}" /> Style="{DynamicResource {x:Static h:ButtonKeys.BorderBrushTransparent}}" />
<Button Grid.Column="1" Content="全部取消" <Button Grid.Column="1" Content="全部取消"
Style="{DynamicResource {x:Static h:ButtonKeys.BorderBrushTransparent}}" /> Style="{DynamicResource {x:Static h:ButtonKeys.BorderBrushTransparent}}" />

View File

@ -19,12 +19,16 @@ namespace Hopetry.ViewModel.Send;
public class DownViewModel : MvcViewModelBase public class DownViewModel : MvcViewModelBase
{ {
private readonly MinioService _minioService; private readonly MinioService _minioService;
public RelayCommand<MinioDownloadTask> OpenDownItemFolder { get; set; } public RelayCommand<MinioDownloadTask> OpenDownItemFolder { get; set; }
private readonly ConcurrentQueue<MinioDownloadTask> _taskQueue = new(); private readonly ConcurrentQueue<MinioDownloadTask> _taskQueue = new();
private SemaphoreSlim _concurrencySemaphore; private SemaphoreSlim _concurrencySemaphore;
private CancellationTokenSource _processingCts = new(); private CancellationTokenSource _processingCts = new();
public int count { get; set; }
private SemaphoreSlim _semaphore;
private readonly ReaderWriterLockSlim _lock = new();
/// <summary> /// <summary>
/// 全部任务动态标题 /// 全部任务动态标题
@ -66,8 +70,8 @@ public class DownViewModel : MvcViewModelBase
} }
// 绑定属性 // 绑定属性
public ObservableCollection<MinioDownloadTask> AllTasks { get;set; } = new(); public ObservableCollection<MinioDownloadTask> AllTasks { get; set; } = new();
public ICollectionView RunningTasksView { get; set; } public ICollectionView RunningTasksView { get; set; }
public ICollectionView FinishedTasksView { get; set; } public ICollectionView FinishedTasksView { get; set; }
@ -82,7 +86,7 @@ public class DownViewModel : MvcViewModelBase
if (_maxConcurrent == value) return; if (_maxConcurrent == value) return;
_maxConcurrent = value; _maxConcurrent = value;
AdjustConcurrency(); //AdjustConcurrency();
RaisePropertyChanged(); RaisePropertyChanged();
} }
} }
@ -100,37 +104,25 @@ public class DownViewModel : MvcViewModelBase
} }
// 命令 // 命令
public ICommand AddTaskCommand { get; } public ICommand AddTaskCommand { get; set; }
public ICommand ClearFinishedCommand { get; } public ICommand ClearFinishedCommand { get; }
private ObservableCollection<DownItem> _downItems;
public ObservableCollection<DownItem> DownItems
{
get => _downItems;
set
{
_downItems = value;
RaisePropertyChanged();
}
}
/// <summary> /// <summary>
/// 构造函数 /// 构造函数
/// </summary> /// </summary>
/// <param name="minioService"></param> /// <param name="minioService"></param>
public DownViewModel(MinioService minioService) public DownViewModel(MinioService minioService, SemaphoreSlim semaphore)
{ {
_minioService = minioService; _minioService = minioService;
_semaphore = semaphore;
Console.WriteLine("初始化DownViewModel"); Console.WriteLine("初始化DownViewModel");
var client = SqlSugarConfig.GetSqlSugarScope(); using var client = SqlSugarConfig.GetSqlSugarScope();
var data = client.Ado.SqlQuery<MinioDownloadTask>($"select * from download_task"); var data = client.Ado.SqlQuery<MinioDownloadTask>($"select * from download_task");
//DownItems = new ObservableCollection<DownItem>(data); //DownItems = new ObservableCollection<DownItem>(data);
AllTasks = new ObservableCollection<MinioDownloadTask>(data); AllTasks = new ObservableCollection<MinioDownloadTask>(data);
AllTaskHeader = $"全部({AllTasks.Count}"; AllTaskHeader = $"全部({AllTasks.Count}";
Console.WriteLine(JsonConvert.SerializeObject(data)); //Console.WriteLine(JsonConvert.SerializeObject(data));
OpenDownItemFolder = new RelayCommand<MinioDownloadTask>(DoOpenDownItemFolder); OpenDownItemFolder = new RelayCommand<MinioDownloadTask>(DoOpenDownItemFolder);
// 命令初始化 // 命令初始化
// AddTaskCommand = new RelayCommand(AddTask, CanAddTask); // AddTaskCommand = new RelayCommand(AddTask, CanAddTask);
@ -166,27 +158,34 @@ public class DownViewModel : MvcViewModelBase
RunningTasksView.Refresh(); RunningTasksView.Refresh();
FinishedTasksView.Refresh(); FinishedTasksView.Refresh();
}; };
ClearFinishedCommand = new CustomCommand(() => ClearFinishedCommand = new CustomCommand(() =>
{ {
// todo 删除已完成的下载记录 // todo 删除已完成的下载记录
}); });
// 启动任务处理线程
_semaphore = new SemaphoreSlim(_maxConcurrent);
// 启动任务处理线程 // 启动任务处理线程
_concurrencySemaphore = new SemaphoreSlim(_maxConcurrent); _concurrencySemaphore = new SemaphoreSlim(_maxConcurrent);
new Thread(ProcessTasksLoop) { IsBackground = true }.Start(); new Thread(ProcessTasksLoop) { IsBackground = true }.Start();
AddTaskCommand = new ActionCommand(AddTask);
} }
private void ProcessTasksLoop() private void ProcessTasksLoop()
{ {
while (!_processingCts.IsCancellationRequested) while (!_processingCts.IsCancellationRequested)
{ {
_semaphore.WaitAsync();
// 按当前并发数启动任务 // 按当前并发数启动任务
for (int i = 0; i < _maxConcurrent; i++) for (int i = 0; i < _maxConcurrent; i++)
{ {
if (_taskQueue.TryDequeue(out var task)) if (_taskQueue.TryDequeue(out var task))
{ {
_ = ExecuteTaskAsync(task); // 启动新线程执行下载任务
_ = Task.Run(() => ExecuteTaskAsync(task));
} }
} }
@ -196,30 +195,25 @@ public class DownViewModel : MvcViewModelBase
private void AddTask() private void AddTask()
{ {
var task = new MinioDownloadTask(_minioService, NewBucket, NewObjectName); string[] objectKeys = { "demo/06.mp4", "demo/07.mp4", "demo/08.mp4", "demo/10.mp4" };
var task = new MinioDownloadTask(_minioService, "demo", objectKeys[count++]);
using var client = SqlSugarConfig.GetSqlSugarScope();
client.Insertable(task).ExecuteCommandIdentityIntoEntity();
AllTasks.Add(task); AllTasks.Add(task);
_taskQueue.Enqueue(task); _taskQueue.Enqueue(task);
RaisePropertyChanged(nameof(NewBucket));
RaisePropertyChanged(nameof(NewObjectName));
} }
private bool CanAddTask() => !string.IsNullOrEmpty(NewBucket) && !string.IsNullOrEmpty(NewObjectName);
private async Task ExecuteTaskAsync(MinioDownloadTask task) private async Task ExecuteTaskAsync(MinioDownloadTask task)
{ {
RunningTasks++;
try try
{ {
// todo 配置下载路径 await task.StartDownload();
var savePath = Path.Combine(task.FileName); // todo 任务完成下载
await task.StartDownload(savePath);
} }
finally finally
{ {
RunningTasks--; _semaphore.Release();
// 任务完成后自动处理下一个
if (!_processingCts.IsCancellationRequested)
ProcessTasksLoop();
} }
} }
@ -239,37 +233,24 @@ public class DownViewModel : MvcViewModelBase
} }
} }
// 绑定属性
private string _newBucket;
public string NewBucket
{
get => _newBucket;
set
{
_newBucket = value;
RaisePropertyChanged();
}
}
private string _newObjectName;
public string NewObjectName
{
get => _newObjectName;
set
{
_newObjectName = value;
RaisePropertyChanged();
}
}
public void DoOpenDownItemFolder(MinioDownloadTask para) public void DoOpenDownItemFolder(MinioDownloadTask para)
{ {
Console.WriteLine($"点击了什么值:{JsonConvert.SerializeObject(para)}"); Console.WriteLine($"点击item值{JsonConvert.SerializeObject(para)}");
Process.Start("explorer.exe", para.FilePath); Process.Start("explorer.exe", para.FilePath);
} }
public void DoCancelDownItem(MinioDownloadTask item)
{
Console.WriteLine("取消下载");
// todo 实现取消下载
}
public void DoPauseDownItem(MinioDownloadTask item)
{
Console.WriteLine("暂停下载");
// todo 实现暂停下载
}
protected override void Init() protected override void Init()
{ {
} }
@ -277,60 +258,4 @@ public class DownViewModel : MvcViewModelBase
protected override void Loaded(string args) protected override void Loaded(string args)
{ {
} }
[SugarTable("f_down_item")]
public class DownItem
{
public DownItem()
{
}
[SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "id")]
public long Id { get; set; }
// 进度 已下载 多久下载完成 下载速度
[SugarColumn(IsIgnore = true)] public int ProgressInt { get; set; }
/// <summary>
/// object key
/// </summary>
[SugarColumn(ColumnName = "object_key")]
public string ObjectKey { get; set; }
/// <summary>
/// 文件名称
/// </summary>
[SugarColumn(ColumnName = "file_name")]
public string FileName { get; set; }
/// <summary>
/// 文件类型
/// </summary>
[SugarColumn(ColumnName = "file_type")]
public string FileType { get; set; }
/// <summary>
/// 文件大小
/// </summary>
[SugarColumn(ColumnName = "file_size")]
public long FileSize { get; set; }
/// <summary>
/// 速度 100kb/s
/// </summary>
[SugarColumn(IsIgnore = true)]
public long Speed { get; set; }
/// <summary>
/// 本地保存路径
/// </summary>
[SugarColumn(ColumnName = "file_path")]
public string FilePath { get; set; }
/// <summary>
/// 文件的ETag
/// </summary>
[SugarColumn(ColumnName = "file_etag")]
public string FileETag { get; set; }
}
} }