603 lines
19 KiB
C#
603 lines
19 KiB
C#
using System.Collections.Concurrent;
|
||
using System.Collections.ObjectModel;
|
||
using System.ComponentModel;
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Windows;
|
||
using System.Windows.Data;
|
||
using System.Windows.Input;
|
||
using HeBianGu.App.Disk;
|
||
using HeBianGu.Base.WpfBase;
|
||
using HeBianGu.Service.Mvc;
|
||
using Hopetry.Models;
|
||
using Hopetry.Provider;
|
||
using Hopetry.Services;
|
||
using Path = System.IO.Path;
|
||
|
||
namespace Hopetry.ViewModel.Send;
|
||
|
||
[ViewModel("Down")]
|
||
public class DownViewModel : MvcViewModelBase
|
||
{
|
||
private readonly MinioService _minioService;
|
||
|
||
public RelayCommand<MinioDownloadTask> OpenDownItemFolder { get; set; }
|
||
public RelayCommand<MinioDownloadTask> CancelDownload { get; set; }
|
||
private readonly ConcurrentQueue<MinioDownloadTask> _taskQueue = new();
|
||
private SemaphoreSlim _semaphore;
|
||
|
||
private CancellationTokenSource _processingCts = new();
|
||
public int count { get; set; }
|
||
private readonly ReaderWriterLockSlim _lock = new();
|
||
|
||
// 下载中 功能按钮显示
|
||
private Visibility _tab0Visibility;
|
||
|
||
public Visibility Tab0Visibility
|
||
{
|
||
get => _tab0Visibility;
|
||
set
|
||
{
|
||
_tab0Visibility = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
private Visibility _tab1Visibility;
|
||
|
||
public Visibility Tab1Visibility
|
||
{
|
||
get => _tab1Visibility;
|
||
set
|
||
{
|
||
_tab1Visibility = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
private int _tabIndex;
|
||
|
||
// tab 索引
|
||
public int TabIndex
|
||
{
|
||
get => _tabIndex;
|
||
set
|
||
{
|
||
_tabIndex = value;
|
||
if (_tabIndex == 0)
|
||
{
|
||
Tab0Visibility = Visibility.Visible;
|
||
Tab1Visibility = Visibility.Hidden;
|
||
}
|
||
else if (_tabIndex == 1)
|
||
{
|
||
Tab0Visibility = Visibility.Hidden;
|
||
Tab1Visibility = Visibility.Visible;
|
||
}
|
||
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 全部任务动态标题
|
||
/// </summary>
|
||
private string _allTaskHeader;
|
||
|
||
public string AllTaskHeader
|
||
{
|
||
get => _allTaskHeader;
|
||
set
|
||
{
|
||
_allTaskHeader = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
private string _runningTaskHeader;
|
||
|
||
public string RunningTaskHeader
|
||
{
|
||
get => _runningTaskHeader;
|
||
set
|
||
{
|
||
_runningTaskHeader = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
private string _finishedTaskHeader;
|
||
|
||
public string FinishedTaskHeader
|
||
{
|
||
get => _finishedTaskHeader;
|
||
set
|
||
{
|
||
_finishedTaskHeader = value;
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
// 绑定属性
|
||
//public ObservableCollection<MinioDownloadTask> AllTasks { get; set; }
|
||
private ObservableCollection<MinioDownloadTask> _runningTasks;
|
||
|
||
public ObservableCollection<MinioDownloadTask> RunningTasks
|
||
{
|
||
get => _runningTasks;
|
||
set
|
||
{
|
||
_runningTasks = value;
|
||
RaisePropertyChanged(); // 触发INotifyPropertyChanged通知
|
||
}
|
||
}
|
||
|
||
private ObservableCollection<MinioDownloadTask> _finishedTasks;
|
||
|
||
public ObservableCollection<MinioDownloadTask> FinishedTasks
|
||
{
|
||
get => _finishedTasks;
|
||
set
|
||
{
|
||
_finishedTasks = value;
|
||
RaisePropertyChanged(); // 触发INotifyPropertyChanged通知
|
||
}
|
||
}
|
||
|
||
private CollectionViewSource _finishedTasksViewSource;
|
||
|
||
public CollectionViewSource FinishedTasksViewSource
|
||
{
|
||
get => _finishedTasksViewSource;
|
||
set
|
||
{
|
||
_finishedTasksViewSource = value;
|
||
RaisePropertyChanged(); // 触发INotifyProperty
|
||
}
|
||
}
|
||
|
||
private int _maxConcurrent;
|
||
|
||
public int MaxConcurrent
|
||
{
|
||
get => _maxConcurrent;
|
||
set
|
||
{
|
||
if (value < 1) value = 1;
|
||
if (_maxConcurrent == value) return;
|
||
var delta = value - _maxConcurrent;
|
||
_maxConcurrent = value;
|
||
AdjustConcurrency(delta);
|
||
RaisePropertyChanged();
|
||
}
|
||
}
|
||
|
||
public ICommand ClearFinishedCommand { get; }
|
||
public RelayCommand<MinioDownloadTask> StartOrPauseDown { get; }
|
||
public ICommand PauseAllTask { get; }
|
||
|
||
public ICommand StopAllTask { get; }
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
/// <param name="minioService"></param>
|
||
public DownViewModel(MinioService minioService)
|
||
{
|
||
// 下载服务
|
||
_minioService = minioService;
|
||
// 暂时未使用 似乎不太有用
|
||
FinishedTasksViewSource = new CollectionViewSource { Source = FinishedTasks };
|
||
FinishedTasksViewSource.SortDescriptions.Add(new SortDescription("FinishedTime", ListSortDirection.Descending));
|
||
Console.WriteLine("初始化DownViewModel");
|
||
using var client = SqlSugarConfig.GetSqlSugarScope();
|
||
OpenDownItemFolder = new RelayCommand<MinioDownloadTask>(DoOpenDownItemFolder);
|
||
// 取消下载
|
||
CancelDownload = new RelayCommand<MinioDownloadTask>(DoCancelDownload);
|
||
// 暂停下载或者开启下载
|
||
StartOrPauseDown = new RelayCommand<MinioDownloadTask>(DoStartOrPauseDown);
|
||
// 全部暂停
|
||
PauseAllTask = new CustomCommand(DoPauseAllTask);
|
||
// 全部取消
|
||
StopAllTask = new CustomCommand(DoStopAllTask);
|
||
// 加载已完成任务记录
|
||
LoadFinishedTasks();
|
||
// 加载未完成任务
|
||
LoadRunningTasks();
|
||
ClearFinishedCommand = new CustomCommand(DoClearFinishedCommand);
|
||
|
||
Console.WriteLine($"初始下载任务并发数:{DownloadSetting.Instance.MaxConcurrent}");
|
||
// 启动任务处理线程
|
||
_maxConcurrent = DownloadSetting.Instance.MaxConcurrent;
|
||
_semaphore = new SemaphoreSlim(DownloadSetting.Instance.MaxConcurrent);
|
||
Console.WriteLine($"初始化信号量,信号源数量{_semaphore.CurrentCount}");
|
||
DownloadSetting.Instance.PropertyChanged += (sender, args) =>
|
||
{
|
||
Console.WriteLine($"当前最大下载任务并发数:{MaxConcurrent}");
|
||
// todo 检查值是否保存了
|
||
Console.WriteLine($"任务并发数新值:{DownloadSetting.Instance.MaxConcurrent}");
|
||
MaxConcurrent = DownloadSetting.Instance.MaxConcurrent;
|
||
};
|
||
new Thread(ProcessTasksLoop) { IsBackground = true }.Start();
|
||
}
|
||
|
||
// 停止所有任务
|
||
public void DoStopAllTask()
|
||
{
|
||
_taskQueue.Clear();
|
||
foreach (var minioDownloadTask in RunningTasks)
|
||
{
|
||
//Console.WriteLine("取消下载");
|
||
|
||
// 取消操作
|
||
if (minioDownloadTask.StopDownTs != null)
|
||
{
|
||
minioDownloadTask.StopDownTs.Cancel();
|
||
}
|
||
|
||
minioDownloadTask.Status = "删除中";
|
||
using var client = SqlSugarConfig.GetSqlSugarScope();
|
||
client.Deleteable<MinioDownloadTask>().Where(x => x.TaskId == minioDownloadTask.TaskId).ExecuteCommand();
|
||
}
|
||
|
||
RunningTasks.Clear();
|
||
RefreshHeader();
|
||
MessageProxy.Snacker.Show("取消下载成功");
|
||
}
|
||
|
||
// 暂停所有任务
|
||
private void DoPauseAllTask()
|
||
{
|
||
_taskQueue.Clear();
|
||
Thread.Sleep(100);
|
||
foreach (var minioDownloadTask in RunningTasks)
|
||
{
|
||
//暂停下载
|
||
if (minioDownloadTask.StopDownTs != null)
|
||
{
|
||
minioDownloadTask.StopDownTs.Cancel();
|
||
}
|
||
|
||
minioDownloadTask.Status = "已暂停";
|
||
minioDownloadTask.StartOrPauseIcon = "\xe748";
|
||
// 速度及剩余时间(视图显示信息)
|
||
minioDownloadTask.Speed = "已暂停";
|
||
}
|
||
}
|
||
|
||
private void DoStartOrPauseDown(MinioDownloadTask item)
|
||
{
|
||
if (item.Status is "下载中" or "等待中")
|
||
{
|
||
if (item.StopDownTs != null)
|
||
{
|
||
//暂停下载
|
||
item.StopDownTs.Cancel();
|
||
}
|
||
|
||
item.Status = "已暂停";
|
||
item.StartOrPauseIcon = "\xe748";
|
||
// 速度及剩余时间(视图显示信息)
|
||
item.Speed = "已暂停";
|
||
// 避免结构函数加载的任务报错
|
||
}
|
||
else if (item.Status == "已暂停")
|
||
{
|
||
item.Status = "等待中";
|
||
// 更新排队时,下载信息
|
||
item.Speed = "等待中";
|
||
item.StartOrPauseIcon = "\xe76e";
|
||
// 加入下载等待队列
|
||
_taskQueue.Enqueue(item);
|
||
}
|
||
}
|
||
|
||
private void DoClearFinishedCommand()
|
||
{
|
||
using var client = SqlSugarConfig.GetSqlSugarScope();
|
||
client.Deleteable<MinioDownloadTask>().Where(x => x.Status == "已完成").ExecuteCommand();
|
||
FinishedTasks.Clear();
|
||
FinishedTaskHeader = $"已完成(0)";
|
||
}
|
||
|
||
public void RefreshHeader()
|
||
{
|
||
RunningTaskHeader = $"下载中({RunningTasks.Count})";
|
||
ViewModelLocator.SendViewModel.DownLinkAction.DisplayName = $"下载 {RunningTasks.Count}";
|
||
FinishedTaskHeader = $"已完成({FinishedTasks.Count})";
|
||
}
|
||
|
||
public void LoadRunningTasks()
|
||
{
|
||
// todo 队列中有几种任务状态 数据库中应该有几种任务状态
|
||
//
|
||
using var client = SqlSugarConfig.GetSqlSugarScope();
|
||
var data = client.Ado
|
||
.SqlQuery<MinioDownloadTask>(
|
||
$"select * from download_task where status='下载中' or status='等待中' or status='已暂停' order by task_id desc")
|
||
.Select(x => new MinioDownloadTask
|
||
{
|
||
Status = "已暂停",
|
||
TaskId = x.TaskId,
|
||
FileName = x.FileName,
|
||
BucketName = x.BucketName,
|
||
ObjectKey = x.ObjectKey,
|
||
TotalSize = x.TotalSize,
|
||
Downloaded = x.Downloaded,
|
||
FilePath = x.FilePath,
|
||
CreateTime = x.CreateTime,
|
||
FinishedTime = x.FinishedTime,
|
||
FileSize = x.FileSize,
|
||
FileETag = x.FileETag,
|
||
FileNumber = x.FileNumber,
|
||
DownloadNumber = x.DownloadNumber,
|
||
FileIcon = x.FileIcon,
|
||
Type = x.Type,
|
||
// 初始化显示
|
||
DownloadInfo = x.Type == 0
|
||
? (x.Downloaded > 1048576 ? $"{x.Downloaded / 1048576:f2}MB" : $"{x.Downloaded / 1024:f2}KB") +
|
||
$"/{x.FileSize}"
|
||
: $"{x.DownloadNumber}/{x.FileNumber}",
|
||
Speed = "已暂停",
|
||
StartOrPauseIcon = "\xe748"
|
||
}).ToList();
|
||
RunningTasks = new ObservableCollection<MinioDownloadTask>(data);
|
||
RunningTaskHeader = $"下载中({RunningTasks.Count})";
|
||
}
|
||
|
||
public void LoadFinishedTasks()
|
||
{
|
||
using var client = SqlSugarConfig.GetSqlSugarScope();
|
||
var data = client.Ado
|
||
.SqlQuery<MinioDownloadTask>(
|
||
$"select * from download_task where status='已完成' order by datetime(finished_time) desc");
|
||
FinishedTasks = new ObservableCollection<MinioDownloadTask>(data);
|
||
FinishedTaskHeader = $"已完成({FinishedTasks.Count})";
|
||
}
|
||
|
||
public void AddTask(string bucketName, string objectKey, string size, string downDir)
|
||
{
|
||
var task = new MinioDownloadTask(_minioService, bucketName, objectKey, downDir, size);
|
||
using var client = SqlSugarConfig.GetSqlSugarScope();
|
||
client.Insertable(task).ExecuteCommandIdentityIntoEntity();
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
RunningTasks.Add(task);
|
||
RefreshHeader();
|
||
});
|
||
Console.WriteLine($"文件大小:{size} ");
|
||
_taskQueue.Enqueue(task);
|
||
}
|
||
|
||
private async void ProcessTasksLoop()
|
||
{
|
||
while (!_processingCts.IsCancellationRequested)
|
||
{
|
||
//Console.WriteLine($"申请前信号源数量:{_semaphore.CurrentCount}");
|
||
await _semaphore.WaitAsync(); // 应该是在这里堵塞了
|
||
//Console.WriteLine($"当前信号源数量:{_semaphore.CurrentCount}");
|
||
// 不应该卡到这里吗???
|
||
if (_taskQueue.TryDequeue(out var task))
|
||
{
|
||
Console.WriteLine("申请下载许可成功!马上开启下载");
|
||
// 启动新线程执行下载任务
|
||
// _ = Task.Run(() => ExecuteTaskAsync(task));
|
||
_ = ExecuteTaskAsync(task).ContinueWith(_ => _semaphore.Release());
|
||
}
|
||
else
|
||
{
|
||
// 队列无内容,马上释放信号源
|
||
_semaphore.Release();
|
||
}
|
||
|
||
// 无任务时,进入低功耗轮询
|
||
Thread.Sleep(500);
|
||
}
|
||
}
|
||
|
||
|
||
private async Task ExecuteTaskAsync(MinioDownloadTask task)
|
||
{
|
||
try
|
||
{
|
||
// minio中好像自带重试机制
|
||
// 补充
|
||
if (task.Minio == null)
|
||
{
|
||
task.Minio = _minioService;
|
||
}
|
||
|
||
if (task.Type == 0)
|
||
{
|
||
await task.StartDownload();
|
||
}
|
||
else
|
||
{
|
||
await task.StartDownloadDir();
|
||
}
|
||
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
RunningTasks.Remove(task);
|
||
FinishedTasks.Add(task);
|
||
RefreshHeader();
|
||
});
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 异常释放信号源
|
||
//_semaphore.Release();
|
||
Console.WriteLine($"下载失败或取消:{ex.Message}");
|
||
Console.WriteLine(ex.StackTrace);
|
||
}
|
||
finally
|
||
{
|
||
if (task.Status.Equals("删除中"))
|
||
{
|
||
Console.WriteLine($"删除文件:{task.FileName}");
|
||
File.Delete(Path.Combine(task.FilePath, task.FileName));
|
||
Console.WriteLine($"{task.FileName}删除成功");
|
||
}
|
||
}
|
||
}
|
||
|
||
private void AdjustConcurrency(int delta)
|
||
{
|
||
Console.WriteLine("AdjustConcurrency 被调用了");
|
||
lock (_semaphore)
|
||
{
|
||
// 调整信号量容量
|
||
if (delta > 0)
|
||
{
|
||
_semaphore.Release(delta);
|
||
}
|
||
else
|
||
{
|
||
var runTask = _runningTasks.Where(task => task is { Status: "下载中", StopDownTs: not null }).ToList();
|
||
Console.WriteLine($"正在下载任务数:{runTask.Count}");
|
||
var num = -delta;
|
||
var temp = new List<MinioDownloadTask>();
|
||
while (_taskQueue.TryDequeue(out var task))
|
||
{
|
||
temp.Add(task);
|
||
}
|
||
_taskQueue.Clear(); // 清空任务队列
|
||
foreach (var item in runTask)
|
||
{
|
||
//Console.WriteLine($"当前可用许可:{_semaphore.CurrentCount}");
|
||
item.StopDownTs.Cancel(); // 暂停任务 释放一个许可
|
||
item.Status = "等待中";
|
||
item.Speed = "等待中";
|
||
// 重新加入下载队列
|
||
temp.Add(item);
|
||
}
|
||
while (num-- > 0)
|
||
{
|
||
_semaphore.Wait();
|
||
}
|
||
foreach (var item in temp)
|
||
{
|
||
_taskQueue.Enqueue(item);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开文件夹
|
||
/// </summary>
|
||
/// <param name="para"></param>
|
||
public void DoOpenDownItemFolder(MinioDownloadTask para)
|
||
{
|
||
switch (para.Type)
|
||
{
|
||
//Console.WriteLine($"点击item值:{JsonConvert.SerializeObject(para)}");
|
||
//Process.Start("explorer.exe", para.FilePath);
|
||
case 0:
|
||
{
|
||
var file = Path.Combine(para.FilePath, para.FileName);
|
||
Process.Start("explorer.exe", $"/select,\"{file}\"");
|
||
break;
|
||
}
|
||
case 1:
|
||
{
|
||
var combinePath = Path.Combine(para.FilePath, para.ObjectKey);
|
||
combinePath = combinePath.Replace("/", "\\");
|
||
Process.Start("explorer.exe", $"/select,\"{combinePath}\"");
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
var path = Path.Combine(para.FilePath, para.BucketName);
|
||
// 存储桶
|
||
Process.Start("explorer.exe", $"/select,\"{path}\"");
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 任务取消
|
||
/// </summary>
|
||
/// <param name="item"></param>
|
||
public void DoCancelDownload(MinioDownloadTask item)
|
||
{
|
||
Console.WriteLine("取消下载");
|
||
|
||
// 取消操作
|
||
if (item.StopDownTs != null)
|
||
{
|
||
item.StopDownTs.Cancel();
|
||
}
|
||
|
||
item.Status = "删除中";
|
||
item.Speed = "删除中";
|
||
using var client = SqlSugarConfig.GetSqlSugarScope();
|
||
client.Deleteable<MinioDownloadTask>().Where(x => x.TaskId == item.TaskId).ExecuteCommand();
|
||
RunningTasks.Remove(item);
|
||
RefreshHeader();
|
||
// 删除下载文件 放到下载里面了
|
||
//File.Delete(Path.Combine(item.FilePath, item.FileName));
|
||
MessageProxy.Snacker.Show($"<{item.FileName}>取消下载");
|
||
}
|
||
|
||
|
||
protected override void Init()
|
||
{
|
||
}
|
||
|
||
protected override void Loaded(string args)
|
||
{
|
||
}
|
||
|
||
public async void AddDirTask(string bucketName, string objectKey, string name,string downDir)
|
||
{
|
||
// 情景1 只选了一个文件夹
|
||
// 情景2 只选了一个存储桶
|
||
// 嵌套情景: 文件夹或者存储桶下,存在文件夹
|
||
var task = new MinioDownloadTask
|
||
{
|
||
FilePath = downDir,
|
||
ObjectKey = objectKey,
|
||
BucketName = bucketName,
|
||
FileName = name, // todo 确定修改这里是否有影响
|
||
Status = "等待中",
|
||
FileIcon = "\xe87a",
|
||
//DownloadInfo = "0/na",
|
||
Speed = "等待中",
|
||
CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
|
||
// 1 文件夹 2 存储桶
|
||
Type = objectKey.Equals(bucketName) ? 2 : 1
|
||
};
|
||
// 存储桶 文件(路径)兼容
|
||
var prefix = objectKey.Equals(bucketName) ? "" : objectKey;
|
||
var x = await _minioService.ListAllObject(bucketName, prefix, true);
|
||
var number = 0;
|
||
await foreach (var item in x)
|
||
{
|
||
// todo 可以记录信息
|
||
number++;
|
||
/*Console.WriteLine("==============");
|
||
Console.WriteLine(item.Key);
|
||
Console.WriteLine(item.Size);
|
||
Console.WriteLine(item.ETag);
|
||
Console.WriteLine(item.LastModified);
|
||
Console.WriteLine(item.IsDir);*/
|
||
}
|
||
|
||
task.FileNumber = number;
|
||
task.FileSize = number + "(个)";
|
||
task.DownloadInfo = $"0/{number}";
|
||
using (var client = SqlSugarConfig.GetSqlSugarScope())
|
||
{
|
||
await client.Insertable(task).ExecuteCommandIdentityIntoEntityAsync();
|
||
}
|
||
|
||
Application.Current.Dispatcher.Invoke(() =>
|
||
{
|
||
RunningTasks.Add(task);
|
||
RefreshHeader();
|
||
});
|
||
_taskQueue.Enqueue(task);
|
||
}
|
||
} |