FieldWorkClient/ViewModel/Send/DownViewModel.cs

603 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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();
}
// 停止所有任务
private 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);
}
}