FieldWorkClient/ViewModel/Send/SendViewModel.cs

729 lines
26 KiB
C#

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Net.Http;
using System.Security.AccessControl;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Threading;
using HeBianGu.Base.WpfBase;
using HeBianGu.General.WpfControlLib;
using HeBianGu.Service.Mvc;
using Hopetry.Provider;
using Hopetry.Provider.Behaviors;
using Hopetry.Services;
using Hopetry.ViewModel.Send;
using Microsoft.Extensions.Configuration;
using Minio.DataModel.Args;
using Minio.DataModel;
using Minio;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Timers;
namespace HeBianGu.App.Disk
{
[ViewModel("Send")]
internal class SendViewModel : MvcViewModelBase
{
private LinkAction _uploadingAction; //正在上传
private LinkAction _completeAction; //上传完成
public LinkAction DownLinkAction;
public LinkAction _waituploadAction;
private readonly FileUploadService _uploadService;
public ICommand WaitUpLoadCommand { get; }
public ICommand DeleteWaitUpLoadCommand { get; }
public bool _isUploading = false;
public string CurrentMinIOPath;
public SendViewModel()
{
_uploadService = new FileUploadService();
// 初始化Timer
_progressTimer = new System.Timers.Timer(1000);
_progressTimer.Elapsed += UpdateProgress;
_heartbeatTimer = new System.Timers.Timer(30_000);
//_heartbeatTimer.Elapsed += async (s, e) => await SendHeartbeatAsync();
DeleteWaitUpLoadCommand = new AsyncRelayCommand(async () => await DeleteFile());
WaitUpLoadCommand = new AsyncRelayCommand(async () => await UploadWaitItems());
systemInfo = SystemInfoCollector.Collect();
//加载待上传列表
GetWaitUploadItems();
//加载上传完成列表
GetCompletedFiles();
}
protected override void Init()
{
ITransitionWipe wipe = new CircleWipe();
DownLinkAction = new LinkAction()
{ Action = "Down", Controller = "Send", DisplayName = "下载 0", Logo = "\xe891", TransitionWipe = wipe };
LinkActions.Add(DownLinkAction);
// 正在上传
_uploadingAction = new LinkAction()
{
Action = "UpLoad",
Controller = "Send",
DisplayName = "正在上传",
Logo = "\xe6f3",
TransitionWipe = wipe
};
LinkActions.Add(_uploadingAction);
_completeAction = new LinkAction()
{
Action = "CompleteUpload",
Controller = "Send",
DisplayName = "上传完成",
Logo = "\xe613",
TransitionWipe = wipe
};
LinkActions.Add(_completeAction);
_waituploadAction = new LinkAction()
{
Action = "WaitUpload",
Controller = "Send",
DisplayName = "待上传",
Logo = "\xe613",
TransitionWipe = wipe
};
LinkActions.Add(_waituploadAction);
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
new Action(() => { SelectLink = LinkActions[0]; }));
UpLoadItems.CollectionChanged += (s, e) => { UpdateUploadingItems(); };
}
protected override void Loaded(string args)
{
}
#region 文件上传
#region 参数
//上传文件总数
private ObservableCollection<UpLoadItems> _upLoadItems = new ObservableCollection<UpLoadItems>();
/// <summary> 说明 </summary>
public ObservableCollection<UpLoadItems> UpLoadItems
{
get { return _upLoadItems; }
set
{
_upLoadItems = value;
//UpdateUploadingItems();
RaisePropertyChanged("UpLoadItems");
}
}
//已上传完成文件
private ObservableCollection<UpLoadItems> _completeItems = new ObservableCollection<UpLoadItems>();
public ObservableCollection<UpLoadItems> CompleteItems
{
get { return _completeItems; }
private set
{
_completeItems = value;
RaisePropertyChanged("CompleteItems");
}
}
//待上传文件
private ObservableCollection<UpLoadItems> _waitUpLoadItems = new ObservableCollection<UpLoadItems>();
/// <summary> 说明 </summary>
public ObservableCollection<UpLoadItems> WaitUpLoadItems
{
get { return _waitUpLoadItems; }
set
{
_waitUpLoadItems = value;
RaisePropertyChanged("WaitUpLoadItems");
}
}
private bool? _isAllSelected;
public bool? IsAllSelected
{
get => _isAllSelected;
set
{
_isAllSelected = value;
// 全选/取消全选逻辑
if (value.HasValue)
{
foreach (var item in WaitUpLoadItems)
{
item.IsSelect = value.Value;
}
}
}
}
// 只读属性,返回过滤后的集合(动态计算)
private ObservableCollection<UpLoadItems> _uploadingItems = new ObservableCollection<UpLoadItems>();
public ObservableCollection<UpLoadItems> UploadingItems
{
get { return _uploadingItems; }
private set
{
_uploadingItems = value;
RaisePropertyChanged("UploadingItems");
}
}
// 在 UpLoadItems 变化时更新 UploadingItems
public void UpdateUploadingItems()
{
UploadingItems = new ObservableCollection<UpLoadItems>(
UpLoadItems?.Where(item => item.Value3 != "已完成") ?? Enumerable.Empty<UpLoadItems>()
);
}
//文件上传进度
private double _progress;
public double Progress
{
get { return _progress; }
set
{
if (_progress != value)
{
_progress = value;
RaisePropertyChanged("Progress");
}
}
}
//上传文件个数
private string _filecount;
public string FileCount
{
get { return _filecount; }
set
{
if (_filecount != value)
{
_filecount = value;
RaisePropertyChanged("FileCount");
}
}
}
//上传完成文件个数
private int _uploadingcount;
public int UploadingCount
{
get { return _uploadingcount; }
set
{
if (_uploadingcount != value)
{
_uploadingcount = value;
_uploadingAction.FullName = "(" + _uploadingcount + ")";
RaisePropertyChanged("UploadingCount");
}
}
}
//上传文件个数
private int _completecount;
public int CompleteCount
{
get { return _completecount; }
set
{
if (_completecount != value)
{
_completecount = value;
_completeAction.FullName = "(" + _completecount + ")";
RaisePropertyChanged("CompleteCount");
}
}
}
//等待上传文件个数
private int _waitcount;
public int WaitCount
{
get { return _waitcount; }
set
{
if (_waitcount != value)
{
_waitcount = value;
_waituploadAction.FullName = "(" + _waitcount + ")";
RaisePropertyChanged("WaitCount");
}
}
}
#endregion
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#endregion
#region 删除等待上传文件
private async Task DeleteFile()
{
try
{
var items = WaitUpLoadItems.Where(r => r.IsSelect == true).ToList();
if (items == null || !items.Any())
{
await MessageProxy.Messager.ShowResult("请先选择要删除的项目");
return;
}
var message = $"确定要删除选中的 {items.Count} 个项目吗?";
var result = MessageBox.Show(message, "确认删除", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
List<string> list = new List<string>();
list=items.Select(r => r.Value4).ToList();
var flag= _uploadService.DeleteFiles(list);
WaitUpLoadItems.RemoveAll(r=>r.IsSelect==true);
WaitCount= WaitUpLoadItems.Count;
if (flag == true)
{
MessageBox.Show($"已成功删除 {items.Count} 个项目");
IsAllSelected = false;
}
else
{
MessageBox.Show($"删除失败");
}
}
}
catch (Exception ex)
{
MessageBox.Show($"删除失败: {ex.Message}");
}
}
#endregion
#region 上传待上传文件
public async Task UploadWaitItems()
{
var items = WaitUpLoadItems.Where(r => r.IsSelect == true).ToList();
try
{
foreach (var item in items)
{
FileInfo fileInfo = new FileInfo(item.Value5);
string sizeText = fileInfo.Length < 1024 * 1024
? $"{Math.Ceiling((decimal)fileInfo.Length / 1024)}KB"
: $"{Math.Ceiling((decimal)fileInfo.Length / (1024 * 1024))}MB";
UpLoadItems ut = new UpLoadItems();
ut.Value = item.Value;
ut.Value3 = "等待上传";
ut.Value4 = item.Value4; //唯一标识,与数据库一致
ut.Value5 = item.Value5; //文件名称
ut.Value6 = item.Value6; //文件地址
ut.Value9 = item.Value9;
ut.Double1 = fileInfo.Length;
ut.Double2 = 0.0;
ut.Bool1 = false;
ut.Value1 = $"0{(fileInfo.Length < 1024 * 1024 ? "KB" : "MB")}/{sizeText}";
UpLoadItems.Add(ut);
}
_uploadCount = UpLoadItems.Count;
_completeCount = UpLoadItems.Where(r => r.Value3 == "已完成").Count();
FileCount = _completeCount + "/" + _uploadCount;
UploadingCount = UploadingItems.Count;
CompleteCount = CompleteItems.Count;
MessageBox.Show("正在上传列表中可查看进度");
WaitUpLoadItems.RemoveAll(r => r.IsSelect == true);
IsAllSelected = false;
WaitCount = WaitUpLoadItems.Count;
// 如果没有上传任务在运行,则启动上传
if (!_isUploading)
{
try
{
await ProcessUploadTasks();
}
catch
{
MessageBox.Show("上传失败");
}
}
}
catch (Exception ex)
{
MessageBox.Show("文件丢失");
}
}
#endregion
#region 获取等待上传列表
public void GetWaitUploadItems()
{
var waitlist = _uploadService.GetAllFiles().Where(r => r.IsComplete == false).ToList();
ObservableCollection<UpLoadItems> up = new ObservableCollection<UpLoadItems>();
if (_isUploading == false)
{
foreach (var file in waitlist)
{
UpLoadItems upLoadItems = new UpLoadItems();
upLoadItems.Value = file.FileName;
upLoadItems.Value4 = file.Id;
upLoadItems.Value5 = file.FilePath;
upLoadItems.Value1 = file.FileSizeText;
upLoadItems.Value6 = file.FileName;
upLoadItems.Value7 = file.CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
upLoadItems.Value9 = file.BucketName;
upLoadItems.Bool1 = false;
up.Add(upLoadItems);
}
WaitUpLoadItems.Clear();
WaitUpLoadItems.AddRange(up);
WaitCount = up.Count;
}
}
#endregion
#region 加载上传完成列表
//加载初始完成文件
public void GetCompletedFiles()
{
var files = _uploadService.GetAllFiles().Where(r => r.IsComplete == true).ToList();
ObservableCollection<UpLoadItems> up = new ObservableCollection<UpLoadItems>();
foreach (var file in files)
{
UpLoadItems upLoadItems = new UpLoadItems();
upLoadItems.Value = file.FileName;
upLoadItems.Value4 = file.Id;
upLoadItems.Value5 = file.FilePath;
upLoadItems.Value1 = file.FileSizeText;
upLoadItems.Value6 = file.FileName;
upLoadItems.Value7 = file.CompleteTime.ToString("yyyy-MM-dd HH:mm:ss");
upLoadItems.Value9 = file.BucketName;
up.Add(upLoadItems);
}
CompleteItems.Clear();
CompleteItems.AddRange(up);
regionCount = up.Count;
CompleteCount = regionCount;
}
#endregion
#region 上传
//private SendViewModel _sendViewModel;
private SemaphoreSlim _semaphore = new SemaphoreSlim(5);
private System.Timers.Timer _progressTimer;
private System.Timers.Timer _heartbeatTimer;
private bool _isTimerRunning = false;
private object _timerLock = new object();
private Task _currentUploadTask = null; // 新增:当前上传任务
private CancellationTokenSource _uploadCancellation = new CancellationTokenSource();
public int _uploadCount = 0;
public int _completeCount = 0;
private int regionCount = 0;
private ExplorerMinIOBehavior _explorerBehavior;
private Hopetry.Provider.SystemInfo systemInfo;
// 配置Redis连接
//private RedisService redis = new RedisService("175.27.168.120:6050,password=HopetryRedis1406,connectRetry=3");
#region 更新总进度条 每隔1s更新一次
private void UpdateProgress(object sender, ElapsedEventArgs e)
{
if (UpLoadItems.Count == 0) return;
double currentBytes = UpLoadItems.Sum(r => r.Double2);
double totalBytes = UpLoadItems.Sum(r => r.Double1);
double progress = Math.Round((currentBytes / totalBytes) * 100, 2);
Application.Current.Dispatcher.Invoke(() => { Progress = progress; });
}
//上传redis心跳检测
//private async Task SendHeartbeatAsync()
//{
// try
// {
// // 上报心跳并更新客户端信息
// //await redis.StoreClientInfoAsync(systemInfo);
// await redis.UpdateClientHeartbeatAsync(systemInfo.MachineId);
// }
// catch (Exception ex)
// {
// // 处理网络异常等
// Debug.WriteLine($"心跳上报失败: {ex.Message}");
// }
//}
private void StartProgressTimer()
{
lock (_timerLock)
{
if (!_isTimerRunning)
{
_progressTimer.Start();
_heartbeatTimer.Start();
_isTimerRunning = true;
}
}
}
private void StopProgressTimer()
{
lock (_timerLock)
{
if (_isTimerRunning)
{
_progressTimer.Stop();
_heartbeatTimer.Stop();
_isTimerRunning = false;
}
}
}
#endregion
//创建上传任务
public async Task ProcessUploadTasks()
{
_isUploading = true;
StartProgressTimer();
// 收集系统信息
//var systemInfo = SystemInfoCollector.Collect();
try
{
// 存储到Redis
//await redis.StoreClientInfoAsync(systemInfo);
while (true)
{
//获取所有未完成的上传项
var pendingItems = UpLoadItems
.Where(item => !item.Bool1)
.ToList();
if (!pendingItems.Any())
break; // 没有待上传文件,退出循环
//创建并运行上传任务
_currentUploadTask = Task.WhenAll(pendingItems.Select(async item =>
{
await _semaphore.WaitAsync(_uploadCancellation.Token);
try
{
await UploadFileToMinIOWithProgress(item);
}
finally
{
_semaphore.Release();
}
}));
await _currentUploadTask;
}
}
catch (Exception ex)
{
//上传被取消
//await redis.DeleteClientAsync(systemInfo.MachineId);
}
finally
{
await Task.Delay(2000);
StopProgressTimer();
// 删除redis信息
//await redis.DeleteClientAsync(systemInfo.MachineId);
_isUploading = false;
UpLoadItems.Clear();
//加载上传完成列表
GetCompletedFiles();
if (MySetting.Instance.IsOn && !UpLoadItems.Any(item => !item.Bool1))
{
Shutdown();
}
}
}
//上传具体执行
private async Task UploadFileToMinIOWithProgress(UpLoadItems ut)
{
ut.Bool1 = true;
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
// 构建配置
var config = builder.Build();
// 获取滑块设置的速度限制(转换为字节 / 秒)
// 查询所有客户端
//var allClients = await redis.GetAllClientsAsync();
//var num = allClients == null ? 1 : allClients.Distinct().Count();
//var speedLimit = 10 * 1024 * 1024 / num/5;
var speedLimit = 100 * 1024 * 1024;
//var speedLimit = Convert.ToInt64(config["Minio:limitspeed"]);
var handler = new MinIOThrottledHandler(speedLimit,
new HttpClientHandler
{
// 保持 MinIO 必需的 SSL 配置
ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true
});
//var handler =
// new HttpClientHandler
// {
// // 保持 MinIO 必需的 SSL 配置
// ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true
// };
try
{
// 从配置获取MinIO设置更安全
IMinioClient client = new MinioClient()
.WithEndpoint(config["Minio:Endpoint"])
.WithCredentials(config["Minio:AccessKey"], config["Minio:SecretKey"])
.WithHttpClient(new HttpClient() { Timeout = TimeSpan.FromMinutes(30) })
.Build();
//string bucketName = GetCurrentBucket();
string bucketName = ut.Value9;
if (string.IsNullOrEmpty(bucketName))
{
throw new Exception("桶不能为空");
}
// 确保桶存在
var beArgs = new BucketExistsArgs().WithBucket(bucketName);
bool found = await client.BucketExistsAsync(beArgs).ConfigureAwait(false);
if (!found)
{
var mbArgs = new MakeBucketArgs().WithBucket(bucketName);
await client.MakeBucketAsync(mbArgs).ConfigureAwait(false);
}
// 关键修改:添加完成信号等待
var completionSource = new TaskCompletionSource<bool>();
var progress = new Progress<ProgressReport>(progressReport =>
{
Application.Current.Dispatcher.InvokeAsync(async () =>
{
ut.Int1 = progressReport.Percentage;
long trans = progressReport.TotalBytesTransferred;
ut.Double2 = trans;
int slashIndex = ut.Value1.IndexOf('/');
string sizePart = ut.Value1.Substring(slashIndex);
string transferredPart = trans < 1024 * 1024
? $"{Math.Ceiling((decimal)trans / 1024)}KB"
: $"{Math.Ceiling((decimal)trans / (1024 * 1024))}MB";
ut.Value1 = $"{transferredPart}{sizePart}";
if (progressReport.Percentage == 100)
{
try
{
await UploadVerifier.VerifyAsync(client, bucketName, ut.Value6);
ut.Value7 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string id = ut.Value4.ToString();
_uploadService.UpdateFileComplete(id, true);
CompleteItems.Add(ut);
ut.Value3 = "已完成";
UpdateUploadingItems();
_completeCount++;
FileCount = _completeCount + "/" + _uploadCount;
UpdateFileCounts();
// 刷新当前目录
_explorerBehavior?.RefreshMinIOPath(CurrentMinIOPath);
}
catch
{
ut.Value3 = $"上传失败";
_uploadService.UpdateFileComplete(ut.Value4.ToString(), false);
}
}
else
{
ut.Value3 = "上传中...";
}
if (progressReport.Percentage == 100)
{
// 延迟500ms确保MinIO完成处理
Task.Delay(500).ContinueWith(_ => { completionSource.TrySetResult(true); });
}
});
});
// 对对象名称进行URL编码
//string objectName = WebUtility.UrlEncode(ut.Value6);
var putObjectArgs = new PutObjectArgs()
.WithBucket(bucketName)
.WithObject(ut.Value6)
.WithFileName(ut.Value5)
.WithProgress(progress);
//.WithObjectSize(5 * 1024 * 1024);
var uploadTask = client.PutObjectAsync(putObjectArgs);
await Task.WhenAny(uploadTask, completionSource.Task);
}
catch (Exception ex)
{
Application.Current.Dispatcher.Invoke(() => { ut.Value3 = $"上传失败: {ex.Message}"; _uploadService.UpdateFileComplete(ut.Value4.ToString(), false); });
}
}
//获取当前路径下的桶
private string GetCurrentBucket()
{
if (string.IsNullOrEmpty(CurrentMinIOPath))
return string.Empty;
return CurrentMinIOPath.Split('/')[0];
}
//更新上传进行中及上传完成列表
private void UpdateFileCounts()
{
UploadingCount = UploadingItems.Count;
CompleteCount = CompleteItems.Count;
}
//关机
private void Shutdown()
{
try
{
Process.Start("shutdown", "/s /t 0");
}
catch (Win32Exception ex)
{
MessageBox.Show($"关机失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
}
}