FieldWorkClient/ViewModel/Send/DownViewModel.cs

555 lines
17 KiB
C#
Raw Normal View History

using System.Collections.Concurrent;
using System.Collections.ObjectModel;
2025-04-22 14:01:48 +08:00
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
2025-04-14 13:52:04 +08:00
using System.Windows;
2025-04-22 14:01:48 +08:00
using System.Windows.Data;
using System.Windows.Input;
2025-04-08 16:22:25 +08:00
using HeBianGu.Base.WpfBase;
using HeBianGu.Service.Mvc;
using Hopetry.Models;
using Hopetry.Provider;
using Hopetry.Services;
using Path = System.IO.Path;
2025-04-08 16:22:25 +08:00
namespace Hopetry.ViewModel.Send;
[ViewModel("Down")]
public class DownViewModel : MvcViewModelBase
{
private readonly MinioService _minioService;
2025-04-12 13:23:12 +08:00
public RelayCommand<MinioDownloadTask> OpenDownItemFolder { get; set; }
public RelayCommand<MinioDownloadTask> CancelDownload { get; set; }
private readonly ConcurrentQueue<MinioDownloadTask> _taskQueue = new();
2025-04-12 15:50:38 +08:00
private SemaphoreSlim _semaphore;
private CancellationTokenSource _processingCts = new();
2025-04-12 13:23:12 +08:00
public int count { get; set; }
private readonly ReaderWriterLockSlim _lock = new();
2025-04-14 13:52:04 +08:00
2025-04-24 16:44:43 +08:00
// 下载中 功能按钮显示
2025-04-14 13:52:04 +08:00
private Visibility _tab0Visibility;
2025-04-14 13:52:04 +08:00
public Visibility Tab0Visibility
{
get => _tab0Visibility;
set
{
_tab0Visibility = value;
RaisePropertyChanged();
}
}
private Visibility _tab1Visibility;
2025-04-14 13:52:04 +08:00
public Visibility Tab1Visibility
{
get => _tab1Visibility;
set
{
_tab1Visibility = value;
RaisePropertyChanged();
}
}
2025-04-14 13:52:04 +08:00
private int _tabIndex;
2025-04-24 16:44:43 +08:00
// tab 索引
2025-04-14 13:52:04 +08:00
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>
2025-04-10 14:31:05 +08:00
private string _allTaskHeader;
public string AllTaskHeader
{
2025-04-10 14:31:05 +08:00
get => _allTaskHeader;
set
{
_allTaskHeader = value;
RaisePropertyChanged();
}
}
private string _runningTaskHeader;
public string RunningTaskHeader
{
get => _runningTaskHeader;
set
{
2025-04-10 14:31:05 +08:00
_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;
2025-04-12 13:23:12 +08:00
public ObservableCollection<MinioDownloadTask> RunningTasks
{
get => _runningTasks;
set
{
_runningTasks = value;
RaisePropertyChanged(); // 触发INotifyPropertyChanged通知
}
}
private ObservableCollection<MinioDownloadTask> _finishedTasks;
2025-04-15 14:05:06 +08:00
public ObservableCollection<MinioDownloadTask> FinishedTasks
{
get => _finishedTasks;
set
{
_finishedTasks = value;
RaisePropertyChanged(); // 触发INotifyPropertyChanged通知
}
}
2025-04-15 14:05:06 +08:00
2025-04-22 14:01:48 +08:00
private CollectionViewSource _finishedTasksViewSource;
2025-04-22 14:57:41 +08:00
2025-04-22 14:01:48 +08:00
public CollectionViewSource FinishedTasksViewSource
{
get => _finishedTasksViewSource;
set
{
_finishedTasksViewSource = value;
RaisePropertyChanged(); // 触发INotifyProperty
}
}
private int _maxConcurrent = 3;
public int MaxConcurrent
{
get => _maxConcurrent;
set
{
if (value < 1) value = 1;
if (_maxConcurrent == value) return;
_maxConcurrent = value;
2025-04-12 13:23:12 +08:00
//AdjustConcurrency();
RaisePropertyChanged();
}
}
public ICommand ClearFinishedCommand { get; }
2025-04-15 14:05:06 +08:00
public RelayCommand<MinioDownloadTask> StartOrPauseDown { get; }
2025-04-22 14:57:41 +08:00
public ICommand PauseAllTask { get; }
2025-04-22 14:57:41 +08:00
public ICommand StopAllTask { get; }
2025-04-08 16:22:25 +08:00
2025-04-10 14:31:05 +08:00
/// <summary>
/// 构造函数
/// </summary>
/// <param name="minioService"></param>
public DownViewModel(MinioService minioService)
2025-04-08 16:22:25 +08:00
{
2025-04-24 16:44:43 +08:00
// 下载服务
2025-04-08 16:22:25 +08:00
_minioService = minioService;
2025-04-24 16:44:43 +08:00
// 暂时未使用 似乎不太有用
2025-04-22 14:01:48 +08:00
FinishedTasksViewSource = new CollectionViewSource { Source = FinishedTasks };
FinishedTasksViewSource.SortDescriptions.Add(new SortDescription("FinishedTime", ListSortDirection.Descending));
2025-04-08 16:22:25 +08:00
Console.WriteLine("初始化DownViewModel");
2025-04-12 13:23:12 +08:00
using var client = SqlSugarConfig.GetSqlSugarScope();
OpenDownItemFolder = new RelayCommand<MinioDownloadTask>(DoOpenDownItemFolder);
// 取消下载
CancelDownload = new RelayCommand<MinioDownloadTask>(DoCancelDownload);
2025-04-21 16:42:41 +08:00
// 暂停下载或者开启下载
2025-04-15 14:05:06 +08:00
StartOrPauseDown = new RelayCommand<MinioDownloadTask>(DoStartOrPauseDown);
2025-04-22 14:57:41 +08:00
// 全部暂停
PauseAllTask = new CustomCommand(DoPauseAllTask);
// 全部取消
2025-04-24 16:44:43 +08:00
StopAllTask = new CustomCommand(DoStopAllTask);
// 加载已完成任务记录
LoadFinishedTasks();
2025-04-24 16:44:43 +08:00
// 加载未完成任务
LoadRunningTasks();
ClearFinishedCommand = new CustomCommand(DoClearFinishedCommand);
// 启动任务处理线程
_semaphore = new SemaphoreSlim(MaxConcurrent);
new Thread(ProcessTasksLoop) { IsBackground = true }.Start();
}
2025-04-10 14:31:05 +08:00
2025-04-22 14:57:41 +08:00
// 停止所有任务
private void DoStopAllTask()
{
foreach (var minioDownloadTask in RunningTasks)
{
_taskQueue.Clear();
//Console.WriteLine("取消下载");
2025-04-22 14:57:41 +08:00
// 取消操作
if (minioDownloadTask.StopDownTs != null)
{
minioDownloadTask.StopDownTs.Cancel();
}
2025-04-24 16:44:43 +08:00
minioDownloadTask.Status = "删除中";
2025-04-22 14:57:41 +08:00
using var client = SqlSugarConfig.GetSqlSugarScope();
client.Deleteable<MinioDownloadTask>().Where(x => x.TaskId == minioDownloadTask.TaskId).ExecuteCommand();
}
2025-04-23 09:45:08 +08:00
2025-04-22 14:57:41 +08:00
RunningTasks.Clear();
RefreshHeader();
MessageProxy.Snacker.Show("取消下载成功");
}
// 暂停所有任务
private void DoPauseAllTask()
{
foreach (var minioDownloadTask in RunningTasks)
{
// 清空下载队列
_taskQueue.Clear();
//暂停下载
if (minioDownloadTask.StopDownTs != null)
{
minioDownloadTask.StopDownTs.Cancel();
}
2025-04-24 16:44:43 +08:00
2025-04-22 14:57:41 +08:00
minioDownloadTask.Status = "已暂停";
minioDownloadTask.StartOrPauseIcon = "\xe748";
// 速度及剩余时间(视图显示信息)
minioDownloadTask.Speed = "已暂停";
}
}
2025-04-15 14:05:06 +08:00
private void DoStartOrPauseDown(MinioDownloadTask item)
{
2025-04-24 16:44:43 +08:00
if (item.Status is "下载中" or "等待中")
2025-04-15 14:05:06 +08:00
{
2025-04-23 09:45:08 +08:00
if (item.StopDownTs != null)
{
//暂停下载
item.StopDownTs.Cancel();
}
2025-04-24 16:44:43 +08:00
item.Status = "已暂停";
item.StartOrPauseIcon = "\xe748";
// 速度及剩余时间(视图显示信息)
item.Speed = "已暂停";
// 避免结构函数加载的任务报错
2025-04-15 14:05:06 +08:00
}
else if (item.Status == "已暂停")
{
item.Status = "等待中";
// 更新排队时,下载信息
item.Speed = "等待中";
item.StartOrPauseIcon = "\xe76e";
// 加入下载等待队列
2025-04-15 14:05:06 +08:00
_taskQueue.Enqueue(item);
}
}
private void DoClearFinishedCommand()
{
using var client = SqlSugarConfig.GetSqlSugarScope();
client.Deleteable<MinioDownloadTask>().Where(x => x.Status == "已完成").ExecuteCommand();
FinishedTasks.Clear();
FinishedTaskHeader = $"已完成0";
}
2025-04-14 13:52:04 +08:00
public void RefreshHeader()
{
RunningTaskHeader = $"下载中({RunningTasks.Count}";
2025-04-26 10:53:46 +08:00
ViewModelLocator.SendViewModel.DownLinkAction.DisplayName = $"下载 {RunningTasks.Count}" ;
FinishedTaskHeader = $"已完成({FinishedTasks.Count}";
}
public void LoadRunningTasks()
{
2025-04-15 14:05:06 +08:00
// todo 队列中有几种任务状态 数据库中应该有几种任务状态
//
using var client = SqlSugarConfig.GetSqlSugarScope();
var data = client.Ado
.SqlQuery<MinioDownloadTask>(
2025-04-23 09:45:08 +08:00
$"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,
2025-04-24 16:44:43 +08:00
BucketName = x.BucketName,
2025-04-23 09:45:08 +08:00
ObjectKey = x.ObjectKey,
TotalSize = x.TotalSize,
Downloaded = x.Downloaded,
FilePath = x.FilePath,
CreateTime = x.CreateTime,
FinishedTime = x.FinishedTime,
FileSize = x.FileSize,
FileETag = x.FileETag,
2025-04-24 16:44:43 +08:00
FileNumber = x.FileNumber,
DownloadNumber = x.DownloadNumber,
2025-04-26 14:03:11 +08:00
FileIcon = x.FileIcon,
2025-04-24 16:44:43 +08:00
Type = x.Type,
// 初始化显示
2025-04-24 16:44:43 +08:00
DownloadInfo = x.Type == 0
? (x.Downloaded > 1048576 ? $"{x.Downloaded / 1048576:f2}MB" : $"{x.Downloaded / 1024:f2}KB") +
$"/{x.FileSize}"
: $"{x.DownloadNumber}/{x.FileNumber}",
2025-04-23 09:45:08 +08:00
Speed = "已暂停",
StartOrPauseIcon = "\xe748"
}).ToList();
RunningTasks = new ObservableCollection<MinioDownloadTask>(data);
RunningTaskHeader = $"下载中({RunningTasks.Count}";
}
2025-04-12 13:23:12 +08:00
public void LoadFinishedTasks()
{
using var client = SqlSugarConfig.GetSqlSugarScope();
var data = client.Ado
2025-04-15 14:05:06 +08:00
.SqlQuery<MinioDownloadTask>(
$"select * from download_task where status='已完成' order by datetime(finished_time) desc");
FinishedTasks = new ObservableCollection<MinioDownloadTask>(data);
FinishedTaskHeader = $"已完成({FinishedTasks.Count}";
2025-04-12 15:50:38 +08:00
}
2025-04-12 13:23:12 +08:00
2025-04-12 15:50:38 +08:00
private async void ProcessTasksLoop()
{
while (!_processingCts.IsCancellationRequested)
{
2025-04-12 15:50:38 +08:00
if (_taskQueue.TryDequeue(out var task))
{
// todo 这里有bug, 如何解决???
/*if (task.Status == "已暂停")
2025-04-22 14:01:48 +08:00
{
// 跳过暂停,但之前已加入队列
continue;
}*/
2025-04-22 14:57:41 +08:00
2025-04-14 13:52:04 +08:00
Console.WriteLine("存在可下载任务,正在申请许可...");
await _semaphore.WaitAsync();
Console.WriteLine("申请下载许可成功!马上开启下载");
2025-04-12 15:50:38 +08:00
// 启动新线程执行下载任务
2025-04-12 16:24:15 +08:00
// _ = Task.Run(() => ExecuteTaskAsync(task));
_ = ExecuteTaskAsync(task).ContinueWith(_ => _semaphore.Release());
}
2025-04-14 13:52:04 +08:00
// 无任务时,进入低功耗轮询
Thread.Sleep(500);
}
}
public void AddTask(string bucketName, string objectKey, string size, string downDir)
{
var task = new MinioDownloadTask(_minioService, bucketName, objectKey, downDir, size);
2025-04-12 13:23:12 +08:00
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 Task ExecuteTaskAsync(MinioDownloadTask task)
{
try
{
// minio中好像自带重试机制
// 补充
if (task.Minio == null)
{
task.Minio = _minioService;
}
2025-04-24 16:44:43 +08:00
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}");
}
finally
{
if (task.Status.Equals("删除中"))
{
Console.WriteLine($"删除文件:{task.FileName}");
File.Delete(Path.Combine(task.FilePath, task.FileName));
Console.WriteLine($"{task.FileName}删除成功");
}
}
}
private void AdjustConcurrency()
{
2025-04-12 15:50:38 +08:00
lock (_semaphore)
{
// 调整信号量容量
2025-04-12 15:50:38 +08:00
var delta = _maxConcurrent - _semaphore.CurrentCount;
if (delta > 0)
{
for (int i = 0; i < delta; i++)
{
2025-04-12 15:50:38 +08:00
_semaphore.Release();
}
}
}
}
2025-04-12 13:23:12 +08:00
public void DoOpenDownItemFolder(MinioDownloadTask para)
{
2025-04-26 10:53:46 +08:00
switch (para.Type)
2025-04-24 16:44:43 +08:00
{
2025-04-26 10:53:46 +08:00
//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;
}
2025-04-24 16:44:43 +08:00
}
}
public void DoCancelDownload(MinioDownloadTask item)
{
2025-04-12 13:23:12 +08:00
Console.WriteLine("取消下载");
2025-04-24 16:44:43 +08:00
// 取消操作
if (item.StopDownTs != null)
{
item.StopDownTs.Cancel();
}
2025-04-24 16:44:43 +08:00
item.Status = "删除中";
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}>取消下载");
}
2025-04-22 14:57:41 +08:00
2025-04-08 16:22:25 +08:00
protected override void Init()
{
}
protected override void Loaded(string args)
{
}
2025-04-24 16:44:43 +08:00
public async void AddDirTask(string bucketName, string objectKey, string downDir)
{
// 情景1 只选了一个文件夹
// 情景2 只选了一个存储桶
// 嵌套情景: 文件夹或者存储桶下,存在文件夹
var task = new MinioDownloadTask
{
FilePath = downDir,
ObjectKey = objectKey,
BucketName = bucketName,
FileName = objectKey,
Status = "等待中",
2025-04-26 14:32:35 +08:00
FileIcon = "\xe87a",
//DownloadInfo = "0/na",
2025-04-24 16:44:43 +08:00
Speed = "等待中",
CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
// 1 文件夹 2 存储桶
Type = objectKey.Equals(bucketName) ? 2 : 1
};
2025-04-26 10:53:46 +08:00
// 存储桶 文件(路径)兼容
2025-04-24 16:44:43 +08:00
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 + "(个)";
2025-04-24 16:44:43 +08:00
task.DownloadInfo = $"0/{number}";
2025-04-26 10:53:46 +08:00
using (var client = SqlSugarConfig.GetSqlSugarScope())
{
await client.Insertable(task).ExecuteCommandIdentityIntoEntityAsync();
}
2025-04-24 16:44:43 +08:00
Application.Current.Dispatcher.Invoke(() =>
{
RunningTasks.Add(task);
RefreshHeader();
});
_taskQueue.Enqueue(task);
}
2025-04-08 16:22:25 +08:00
}