FieldWorkClient/ViewModel/Loyout/LoyoutViewModel.cs

992 lines
37 KiB
C#

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Timers;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using HeBianGu.Base.WpfBase;
using HeBianGu.Control.Explorer;
using HeBianGu.Control.Message;
using HeBianGu.Service.Mvc;
using Hopetry.Models;
using Hopetry.Provider;
using Hopetry.Provider.Behaviors;
using Hopetry.Services;
using Hopetry.ViewModel.Send;
using Microsoft.Extensions.Configuration;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;
using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Timer = System.Timers.Timer;
using System.Net.Http;
using System.Net;
using Minio.Exceptions;
using System.Collections.Concurrent;
using System.Security.AccessControl;
using Microsoft.VisualBasic;
namespace HeBianGu.App.Disk
{
[ViewModel("Loyout")]
internal class LoyoutViewModel : MvcViewModelBase
{
#region 参数定义及初始化
private readonly FileUploadService _uploadService;
private string _path;
/// <summary> 说明 </summary>
public string Path
{
get { return _path; }
set
{
_path = value;
RaisePropertyChanged("Path");
}
}
private string _nearPath;
/// <summary> 说明 </summary>
public string NearPath
{
get { return _nearPath; }
set
{
_nearPath = value;
RaisePropertyChanged("NearPath");
}
}
private string _sharePath;
/// <summary> 说明 </summary>
public string SharePath
{
get { return _sharePath; }
set
{
_sharePath = value;
RaisePropertyChanged("SharePath");
}
}
protected override void Init()
{
Path = Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
NearPath = Environment.GetFolderPath(Environment.SpecialFolder.Recent);
SharePath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
// LinkActions.Add(new LinkAction() { Action = "Near", Controller = "Loyout", DisplayName = "最近使用", Logo = "\xe6f3" });
LinkActions.Add(new LinkAction()
{ Action = "Explorer", Controller = "Loyout", DisplayName = "全部文件", Logo = "\xe8d6" });
// LinkActions.Add(new LinkAction() { Action = "Image", Controller = "Loyout", DisplayName = " 图片", Logo = "" });
// LinkActions.Add(new LinkAction() { Action = "Video", Controller = "Loyout", DisplayName = " 视频", Logo = "" });
// LinkActions.Add(new LinkAction() { Action = "Document", Controller = "Loyout", DisplayName = " 文档", Logo = "" });
// LinkActions.Add(new LinkAction() { Action = "Music", Controller = "Loyout", DisplayName = " 音乐", Logo = "" });
// LinkActions.Add(new LinkAction() { Action = "Explorer", Controller = "Loyout", DisplayName = " 种子", Logo = "" });
//LinkActions.Add(new LinkAction() { Action = "Recent", Controller = "Loyout", DisplayName = " 其他", Logo = "" });
// LinkActions.Add(new LinkAction() { Action = "Space", Controller = "Loyout", DisplayName = "隐藏空间", Logo = "\xe613" });
//LinkActions.Add(new LinkAction() { Action = "Share", Controller = "Loyout", DisplayName = "我的分享", Logo = "\xe764" });
// LinkActions.Add(new LinkAction() { Action = "Near", Controller = "Loyout", DisplayName = "回收站", Logo = "\xe618" });
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
new Action(() => { SelectLink = LinkActions[0]; }));
}
protected override void Loaded(string args)
{
GetCompletedFiles();
}
#endregion
#region 文件上传
#region 参数定义
private SendViewModel _sendViewModel;
private IConfiguration config;
private SemaphoreSlim _semaphore = new SemaphoreSlim(5);
private Timer _progressTimer;
private Timer _heartbeatTimer ;
private bool _isTimerRunning = false;
private object _timerLock = new object();
private bool _isUploading = false; // 新增:标记是否有上传任务正在运行
private Task _currentUploadTask = null; // 新增:当前上传任务
private CancellationTokenSource _uploadCancellation = new CancellationTokenSource();
private int _uploadCount = 0;
private int _completeCount = 0;
private int regionCount = 0;
private ExplorerMinIOBehavior _explorerBehavior;
private MinIOSearchBehavior _minioBehavior;
private Hopetry.Provider.SystemInfo systemInfo;
// 配置Redis连接
private RedisService redis = new RedisService("175.27.168.120:6050,password=HopetryRedis1406,connectRetry=3");
public ICommand UploadCommand { get; }
public ICommand UploadCommand1 { get; }
public ICommand CreateFolderCommand { get; }
public ICommand DownloadCommand { get; set; }
public ICommand DeleteFolderCommand { get; }
//public ICommand SelectItemsCommand { get; }
public LoyoutViewModel(SendViewModel sendViewModel)
{
_sendViewModel = sendViewModel;
_uploadService = new FileUploadService();
UploadCommand = new AsyncRelayCommand(async () => await UploadFile());
UploadCommand1 = new AsyncRelayCommand(async () => await UploadFile1());
CreateFolderCommand = new AsyncRelayCommand(async () => await CreateNewFolderAsync());
DeleteFolderCommand = new AsyncRelayCommand(async () => await DeleteSelectedItemsAsync());
DownloadCommand = new AsyncRelayCommand(async () => await DoDownloadCommand());
SelectDirCommand = new CustomCommand(async () => await DoSelectDirCommand());
systemInfo = SystemInfoCollector.Collect();
//SelectItemsCommand = new AsyncRelayCommand(async () => await RelayCommand<SystemInfoModel>(SelectItem)SelectItem());
// 初始化Timer
_progressTimer = new Timer(1000);
_progressTimer.Elapsed += UpdateProgress;
_heartbeatTimer = new Timer(30_000);
_heartbeatTimer.Elapsed += async (s, e) => await SendHeartbeatAsync();
_sendViewModel.UpLoadItems.CollectionChanged += (s, e) => { _sendViewModel.UpdateUploadingItems(); };
//加载上传完成列表
GetCompletedFiles();
}
private async Task DoSelectDirCommand()
{
CommonOpenFileDialog dialog = new CommonOpenFileDialog
{
IsFolderPicker = true
};
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
var folderPath = dialog.FileName; // 获取选中的文件夹路径
// todo 设置文件夹
Application.Current.Dispatcher.Invoke(() =>
{
DownFolderSelect.DownFolder = folderPath;
Console.WriteLine($"选择文件夹:{folderPath}");
});
// 处理选中的文件夹路径
}
}
#endregion
#region 行为方法
// 添加设置Behavior的方法
public void SetExplorerBehavior(ExplorerMinIOBehavior behavior)
{
_explorerBehavior = behavior;
}
public void SetExplorerBehavior(MinIOSearchBehavior behavior)
{
_minioBehavior = behavior;
}
#endregion
#region 更新总进度条 每隔1s更新一次
private void UpdateProgress(object sender, ElapsedEventArgs e)
{
if (_sendViewModel.UpLoadItems.Count == 0) return;
double currentBytes = _sendViewModel.UpLoadItems.Sum(r => r.Double2);
double totalBytes = _sendViewModel.UpLoadItems.Sum(r => r.Double1);
double progress = Math.Round((currentBytes / totalBytes) * 100, 2);
Application.Current.Dispatcher.Invoke(() => { _sendViewModel.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
#region 上传执行
//多文件上传
private async Task UploadFile()
{
var openFileDialog = new OpenFileDialog
{
Multiselect = true,
Filter = "All Files (*.*)|*.*"
};
if (openFileDialog.ShowDialog() == true)
{
// 过滤掉已经存在的文件
var newFiles = openFileDialog.FileNames
.Where(filePath => !_sendViewModel.UpLoadItems.Any(item =>
item.Value5.Equals(filePath, StringComparison.OrdinalIgnoreCase)))
.ToList();
if (newFiles.Count == 0)
{
MessageBox.Show("没有新文件需要上传或文件已在上传队列中");
return;
}
//判断是否存在上传路径
string bucketName = GetCurrentBucket();
if (string.IsNullOrEmpty(bucketName))
{
MessageBox.Show("请选择上传路径");
return;
}
foreach (string filePath in newFiles)
{
var fix = GetCurrentPrefix(); //获取当前所在的文件夹路径,除桶之外的
string str = System.IO.Path.GetFileName(filePath);
if (!string.IsNullOrEmpty(fix))
{
str = fix + System.IO.Path.GetFileName(filePath);
}
var ut = CreateUploadItem(filePath, str);
_sendViewModel.UpLoadItems.Add(ut);
}
_uploadCount = _sendViewModel.UpLoadItems.Count;
//_uploadCount = _sendViewModel.UpLoadItems.Count;
_completeCount = _sendViewModel.UpLoadItems.Where(r => r.Value3 == "已完成").Count();
_sendViewModel.FileCount = _completeCount + "/" + _uploadCount;
UpdateFileCounts();
MessageBox.Show("传输列表中可查看进度");
// 如果没有上传任务在运行,则启动上传
if (!_isUploading)
{
await ProcessUploadTasks();
}
}
}
//文件夹上传
private async Task UploadFile1()
{
var dialog = new CommonOpenFileDialog
{
IsFolderPicker = true,
Title = "请选择上传文件"
};
if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
{
string folderPath = dialog.FileName;
string folderName = new DirectoryInfo(folderPath).Name; // 获取选中的文件夹名称
var files = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories)
.Where(filePath => !_sendViewModel.UpLoadItems.Any(item =>
item.Value5.Equals(filePath, StringComparison.OrdinalIgnoreCase)))
.ToList();
if (files.Count == 0)
{
MessageBox.Show("没有新文件需要上传或文件已在上传队列中");
return;
}
//判断是否存在上传路径
string bucketName = GetCurrentBucket();
if (string.IsNullOrEmpty(bucketName))
{
MessageBox.Show("请选择上传路径");
return;
}
foreach (string filePath in files)
{
var fix = GetCurrentPrefix(); //获取当前所在的文件夹路径,除桶之外的
string relativePath =
folderName + '/' + filePath.Substring(folderPath.Length + 1).Replace('\\', '/');
if (!string.IsNullOrEmpty(fix))
{
relativePath = fix + folderName + '/' +
filePath.Substring(folderPath.Length + 1).Replace('\\', '/');
}
var ut = CreateUploadItem(filePath, relativePath);
_sendViewModel.UpLoadItems.Add(ut);
}
_uploadCount = _sendViewModel.UpLoadItems.Count;
_completeCount = _sendViewModel.UpLoadItems.Where(r => r.Value3 == "已完成").Count();
_sendViewModel.FileCount = _completeCount + "/" + _uploadCount;
UpdateFileCounts();
MessageBox.Show("传输列表中可查看进度");
// 如果没有上传任务在运行,则启动上传
if (!_isUploading)
{
await ProcessUploadTasks();
}
}
}
//创建上传列表
private UpLoadItems CreateUploadItem(string filePath, string objectName)
{
FileInfo fileInfo = new FileInfo(filePath);
string sizeText = fileInfo.Length < 1024 * 1024
? $"{Math.Ceiling((decimal)fileInfo.Length / 1024)}KB"
: $"{Math.Ceiling((decimal)fileInfo.Length / (1024 * 1024))}MB";
//写入数据库
FUpload fp = new FUpload();
fp.Id = Guid.NewGuid().ToString();
fp.FileName = objectName;
fp.FileSize = fileInfo.Length;
fp.FilePath = filePath;
fp.FileType = fileInfo.Extension;
fp.CreateTime = DateTime.Now;
fp.IsComplete = false;
fp.FileSizeText = sizeText;
if (_uploadService.AddFile(fp))
{
return new UpLoadItems
{
Value = System.IO.Path.GetFileName(filePath),
Value3 = "等待上传",
Value4 = fp.Id, //唯一标识,与数据库一致
Value5 = filePath, //文件名称
Value6 = objectName, //文件地址
Double1 = fileInfo.Length,
Double2 = 0.0,
Bool1 = false,
Value1 = $"0{(fileInfo.Length < 1024 * 1024 ? "KB" : "MB")}/{sizeText}"
};
}
else
{
throw new Exception("文件载入失败");
}
}
//创建上传任务
private async Task ProcessUploadTasks()
{
_isUploading = true;
StartProgressTimer();
// 收集系统信息
//var systemInfo = SystemInfoCollector.Collect();
try
{
// 存储到Redis
await redis.StoreClientInfoAsync(systemInfo);
while (true)
{
//获取所有未完成的上传项
var pendingItems = _sendViewModel.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;
_sendViewModel.UpLoadItems.Clear();
//加载上传完成列表
GetCompletedFiles();
if (MySetting.Instance.IsOn && !_sendViewModel.UpLoadItems.Any(item => !item.Bool1))
{
Shutdown();
}
}
}
#region
#endregion
//上传具体执行
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;
//var speedLimit = Convert.ToInt64(config["Minio:limitspeed"]);
var handler = new MinIOThrottledHandler(speedLimit,
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(handler) { Timeout = Timeout.InfiniteTimeSpan })
.Build();
string bucketName = GetCurrentBucket();
// 确保桶存在
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.Invoke(() =>
{
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)
{
ut.Value7 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string id = ut.Value4.ToString();
_uploadService.UpdateFileComplete(id);
_sendViewModel.CompleteItems.Add(ut);
ut.Value3 = "已完成";
_sendViewModel.UpdateUploadingItems();
_completeCount++;
_sendViewModel.FileCount = _completeCount + "/" + _uploadCount;
UpdateFileCounts();
// 刷新当前目录
_explorerBehavior?.RefreshMinIOPath(CurrentMinIOPath);
}
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);
//await UploadVerifier.VerifyAsync(client, bucketName, objectName);
if (uploadTask.IsFaulted)
throw uploadTask.Exception;
}
catch (Exception ex)
{
Application.Current.Dispatcher.Invoke(() => { ut.Value3 = $"上传失败: {ex.Message}"; });
}
}
#endregion
#region 其他
//关机
private void Shutdown()
{
try
{
Process.Start("shutdown", "/s /t 0");
}
catch (Win32Exception ex)
{
MessageBox.Show($"关机失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
//更新上传进行中及上传完成列表
private void UpdateFileCounts()
{
_sendViewModel.UploadingCount = _sendViewModel.UploadingItems.Count;
_sendViewModel.CompleteCount = _sendViewModel.CompleteItems.Count;
}
//加载初始完成文件
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");
up.Add(upLoadItems);
}
_sendViewModel.CompleteItems.Clear();
_sendViewModel.CompleteItems.AddRange(up);
regionCount = up.Count;
_sendViewModel.CompleteCount = regionCount;
}
#endregion
#endregion
#region 文件下载
private DownFolderSelect _downFolderSelect = new DownFolderSelect();
public DownFolderSelect DownFolderSelect
{
get => _downFolderSelect;
set
{
_downFolderSelect = value;
RaisePropertyChanged();
}
}
private async Task DoDownloadCommand()
{
// 当为文件时,处理
var tempSelectedItems = new ObservableCollection<SystemInfoModel>(SelectedItems);
if (tempSelectedItems.Count > 0)
{
if (string.IsNullOrEmpty(DownFolderSelect.DownFolder))
{
// 如果是空,则把同步目录拿过来使用
DownFolderSelect.DownFolder = ViewModelLocator.SyncViewModel.SyncDir;
}
//下载目录
bool r = await MessageProxy.Presenter.Show(DownFolderSelect, x => true, "下载目录选择", x =>
{
x.Width = 600;
x.Height = 450;
//x.Padding = new System.Windows.Thickness(10);
//x.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
//x.VerticalAlignment = System.Windows.VerticalAlignment.Center;
}, ObjectContentDialog.ClearKey);
if (r)
{
var downDir = DownFolderSelect.DownFolder;
if (DownFolderSelect.IsSelect)
{
// todo 持久化配置
}
SelectedItems.Clear();
_explorerBehavior.RefreshMinIOPath(CurrentMinIOPath);
Task.Run(() => AddTaskToDownLoad(tempSelectedItems, downDir));
}
}
else
{
await MessageProxy.Messager.ShowResult("至少选择一项");
}
}
private void AddTaskToDownLoad(ObservableCollection<SystemInfoModel> tempSelectedItems, string downDir)
{
foreach (var item in tempSelectedItems)
{
if (item is MinIOFileModel file)
{
var temp = (MinIOFileInfo)file.Model;
var objectKey = temp.FullName.Replace(temp.BucketName + "/", "");
ViewModelLocator.DownViewModel.AddTask(temp.BucketName, objectKey, temp.Size, downDir);
}
else if (item is MinIODirectoryModel dir)
{
var temp = (MinIODirectoryInfo)dir.Model;
//Console.WriteLine($"bucket: {temp.BucketName} objectKey {temp.FullName}");
var objectKey = temp.FullName.Replace(temp.BucketName + "/", "");
ViewModelLocator.DownViewModel.AddDirTask(temp.BucketName, objectKey, downDir);
}
MessageProxy.Snacker.Show("已加入下载队列");
}
}
#endregion
#region 文件列表
private string _currentMinIOPath;
/// <summary> 说明CurrentMinIOPath </summary>
public string CurrentMinIOPath
{
get { return _currentMinIOPath; }
set
{
// 确保路径格式正确
var newPath = value;
if (!string.IsNullOrEmpty(newPath) && !newPath.EndsWith("/") &&
newPath.Contains("/") && !newPath.EndsWith("/"))
{
newPath += "/";
}
if (_currentMinIOPath != newPath)
{
_currentMinIOPath = newPath;
RaisePropertyChanged("CurrentMinIOPath");
//// 强制刷新导航栏
//if (_explorerBehavior != null)
//{
// _explorerBehavior.RefreshMinIOPath(newPath);
//}
}
}
}
public ObservableCollection<SystemInfoModel> Items { get; } = new ObservableCollection<SystemInfoModel>();
//Explorer创建新建文件夹并可编辑
private async Task CreateNewFolderAsync()
{
try
{
// 创建新的文件夹模型
var newFolder = new MinIODirectoryModel(
new MinIODirectoryInfo(GetCurrentBucket(), "新建文件夹/", false, DateTime.Now))
{
IsRenaming = false // 设置为可编辑状态
};
// 通知行为类开始编辑
_explorerBehavior?.BeginEditNewFolder(newFolder);
await Task.Delay(100); // 等待UI更新
}
catch (Exception ex)
{
MessageBox.Show($"创建文件夹失败: {ex.Message}");
}
}
//minio创建新建文件
public async Task CreateMinIOFolder(string folderName)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
// 构建配置
var config = builder.Build();
// 从配置获取MinIO设置更安全
IMinioClient client = new MinioClient()
.WithEndpoint(config["Minio:Endpoint"])
.WithCredentials(config["Minio:AccessKey"], config["Minio:SecretKey"])
.Build();
if (string.IsNullOrWhiteSpace(folderName))
return;
if (!folderName.EndsWith("/"))
folderName += "/";
try
{
var bucketName = GetCurrentBucket();
var prefix = GetCurrentPrefix();
var beArgs = new BucketExistsArgs().WithBucket(bucketName);
bool found = await client.BucketExistsAsync(beArgs);
if (!found)
{
var mbArgs = new MakeBucketArgs().WithBucket(bucketName);
await client.MakeBucketAsync(mbArgs);
}
using (var emptyStream = new MemoryStream(Encoding.UTF8.GetBytes(" ")))
{
await client.PutObjectAsync(
new PutObjectArgs()
.WithBucket(bucketName)
.WithObject(prefix + folderName)
.WithStreamData(emptyStream)
.WithObjectSize(1) // 必须设置为0
.WithContentType("application/x-directory"));
}
// 刷新当前目录
_explorerBehavior?.RefreshMinIOPath(CurrentMinIOPath);
}
catch (Exception ex)
{
MessageBox.Show($"在MinIO中创建文件夹失败: {ex.Message}");
}
}
//获取当前路径下的桶
private string GetCurrentBucket()
{
if (string.IsNullOrEmpty(CurrentMinIOPath))
return string.Empty;
return CurrentMinIOPath.Split('/')[0];
}
//获取桶下的文件路径
private string GetCurrentPrefix()
{
if (string.IsNullOrEmpty(CurrentMinIOPath) || CurrentMinIOPath.IndexOf('/') < 0)
return string.Empty;
return CurrentMinIOPath.Substring(CurrentMinIOPath.IndexOf('/') + 1);
}
#endregion
#region 删除文件夹
private ObservableCollection<SystemInfoModel> _selectedItems = new ObservableCollection<SystemInfoModel>();
public ObservableCollection<SystemInfoModel> SelectedItems
{
get { return _selectedItems; }
set
{
_selectedItems = value;
RaisePropertyChanged();
}
}
private ICommand _selectItemsCommand;
public ICommand SelectItemsCommand => _selectItemsCommand ??
(_selectItemsCommand = new RelayCommand<SystemInfoModel>(SelectItem));
public ICommand SelectDirCommand { get; set; }
private void SelectItem(SystemInfoModel item)
{
if (item == null) return;
if (SelectedItems.Contains(item))
{
SelectedItems.Remove(item);
}
else
{
SelectedItems.Add(item);
}
}
private async Task DeleteSelectedItemsAsync()
{
try
{
var selectedItems = _selectedItems;
if (selectedItems == null || !selectedItems.Any())
{
await MessageProxy.Messager.ShowResult("请先选择要删除的项目");
return;
}
var message = $"确定要删除选中的 {selectedItems.Count} 个项目吗?";
var result = MessageBox.Show(message, "确认删除", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
await DeleteMinIOItems(selectedItems);
MessageBox.Show($"已成功删除 {selectedItems.Count} 个项目");
_explorerBehavior.RefreshMinIOPath(CurrentMinIOPath);
SelectedItems.Clear();
}
}
catch (Exception ex)
{
MessageBox.Show($"删除失败: {ex.Message}");
}
}
private async Task DeleteMinIOItems(IEnumerable<SystemInfoModel> items)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
var config = builder.Build();
using var client = new MinioClient()
.WithEndpoint(config["Minio:Endpoint"])
.WithCredentials(config["Minio:AccessKey"], config["Minio:SecretKey"])
.Build();
// 按桶分组
var itemsByBucket = items.GroupBy(item =>
{
if (item is MinIOFileModel file)
return ((MinIOFileInfo)file.Model).BucketName;
if (item is MinIODirectoryModel dir)
return ((MinIODirectoryInfo)dir.Model).BucketName;
return null;
});
foreach (var bucketGroup in itemsByBucket.Where(g => g.Key != null))
{
var bucketName = bucketGroup.Key;
var objectsToDelete = new List<string>();
foreach (var item in bucketGroup)
{
if (item is MinIOFileModel fileModel)
{
var fileInfo = (MinIOFileInfo)fileModel.Model;
objectsToDelete.Add(fileInfo.FullName.Substring(fileInfo.BucketName.Length + 1));
}
else if (item is MinIODirectoryModel dirModel)
{
var dirInfo = (MinIODirectoryInfo)dirModel.Model;
var folderPath = dirInfo.FullName.Substring(dirInfo.BucketName.Length + 1);
// 确保文件夹路径以斜杠结尾
if (!folderPath.EndsWith("/")) folderPath += "/";
// 获取文件夹下所有对象
var listArgs = new ListObjectsArgs()
.WithBucket(bucketName)
.WithPrefix(folderPath)
.WithRecursive(true);
await foreach (var obj in client.ListObjectsEnumAsync(listArgs))
{
objectsToDelete.Add(obj.Key);
}
// 添加文件夹标记本身
objectsToDelete.Add(folderPath);
}
}
// 批量删除
if (objectsToDelete.Any())
{
await client.RemoveObjectsAsync(
new RemoveObjectsArgs()
.WithBucket(bucketName)
.WithObjects(objectsToDelete));
}
}
}
#endregion
}
internal class DataFileViewModel : ObservableSourceViewModel<TestViewModel>
{
protected override void Init()
{
base.Init();
}
}
}