FieldWorkClient/ViewModel/Loyout/LoyoutViewModel.cs

797 lines
29 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.ObjectModel;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using HarmonyLib;
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.Args;
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)
{
}
#endregion
#region 文件上传
#region 网盘空间文件数量更新
private string _statusMessage;
public string StatusMessage
{
get { return _statusMessage; }
set
{
_statusMessage = value;
RaisePropertyChanged("StatusMessage");
}
}
// 添加状态更新方法
public void UpdateStatus(string message)
{
StatusMessage = message;
}
#endregion
#region 参数定义
private SendViewModel _sendViewModel;
private IConfiguration config;
//private Task _currentUploadTask = null; // 新增:当前上传任务
//private CancellationTokenSource _uploadCancellation = new CancellationTokenSource();
//private int _uploadCount = 0;
//private int _completeCount = 0;
private ExplorerMinIOBehavior _explorerBehavior;
private MinIOSearchBehavior _minioBehavior;
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());
// 订阅上传完成事件,以便在上传完成后刷新显示
_sendViewModel.FileUploadCompleted += OnFileUploadCompleted;
// 初始化CurrentMinIOPath为空字符串表示显示根目录所有存储桶
CurrentMinIOPath = string.Empty;
//systemInfo = SystemInfoCollector.Collect();
}
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 上传执行
//多文件上传
private async Task UploadFile()
{
var openFileDialog = new OpenFileDialog
{
Multiselect = true,
Filter = "All Files (*.*)|*.*"
};
if (openFileDialog.ShowDialog() == true)
{
MessageProxy.Snacker.Show("正在上传列表中可查看进度");
// 如果没有上传任务在运行,则启动上传
/*if (!_sendViewModel._isUploading)
{
await _sendViewModel.ProcessUploadTasks();
}*/
Console
.WriteLine($"弹窗返回文件数:{openFileDialog.FileNames.Length}");
// 过滤掉已经存在的文件
var newFiles = openFileDialog.FileNames
.Where(filePath => !_sendViewModel.UpLoadItems.Any(item =>
item.Value5.Equals(filePath, StringComparison.OrdinalIgnoreCase)))
.ToList();
Console.WriteLine($"过滤后的文件数:{newFiles.Count}");
if (newFiles.Count == 0)
{
MessageBox.Show("没有新文件需要上传或文件已在上传队列中");
return;
}
//判断是否存在上传路径
var bucketName = GetCurrentBucket();
if (string.IsNullOrEmpty(bucketName))
{
MessageBox.Show("请选择上传路径");
return;
}
var fix = GetCurrentPrefix(); //获取当前所在的文件夹路径,除桶之外的
Task.Run(() =>
{
foreach (string filePath in newFiles)
{
string str = System.IO.Path.GetFileName(filePath);
if (!string.IsNullOrEmpty(fix))
{
str = fix + System.IO.Path.GetFileName(filePath);
}
var ut = CreateUploadItem(filePath, str, bucketName);
_sendViewModel.AddUploadTask(ut);
}
});
}
}
//文件夹上传
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 beforeFiles = Directory.GetFiles(folderPath, "*.*", SearchOption.AllDirectories);
Console.WriteLine($"文件夹上传选中文件数:{beforeFiles.Length}");
var files = beforeFiles
.Where(filePath => !_sendViewModel.UpLoadItems
.Any(item =>
item.Value5.Equals(filePath, StringComparison.OrdinalIgnoreCase)))
.ToList();
Console.WriteLine($"过滤后的文件数:{files.Count}");
if (files.Count == 0)
{
MessageBox.Show("没有新文件需要上传或文件已在上传队列中");
return;
}
//判断是否存在上传路径
string bucketName = GetCurrentBucket();
if (string.IsNullOrEmpty(bucketName))
{
MessageBox.Show("请选择上传路径");
return;
}
MessageProxy.Snacker.Show("正在上传列表中可查看进度");
MessageProxy.Snacker.Show($"添加上传{files.Count}个文件");
Task.Run(() =>
{
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, bucketName);
_sendViewModel.AddUploadTask(ut);
}
});
}
}
//创建上传列表
private UpLoadItems CreateUploadItem(string filePath, string objectName, string bucketname)
{
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;
fp.BucketName = bucketname;
if (_uploadService.AddFile(fp)) //添加到数据库,成功后,添加到下载队列
{
return new UpLoadItems
{
Value = System.IO.Path.GetFileName(filePath),
Value3 = "等待上传",
Value4 = fp.Id, //唯一标识,与数据库一致
Value5 = filePath, //文件名称
Value6 = objectName, //文件地址
Value9 = bucketname,
Double1 = fileInfo.Length,
Double2 = 0.0,
Bool1 = false,
Value1 = $"0{(fileInfo.Length < 1024 * 1024 ? "KB" : "MB")}/{sizeText}"
};
}
throw new Exception("文件载入失败");
}
#endregion
#region 其他
//更新上传进行中及上传完成列表
private void UpdateFileCounts()
{
_sendViewModel.UploadingCount = _sendViewModel.UploadingItems.Count;
_sendViewModel.CompleteCount = _sendViewModel.CompleteItems.Count;
}
#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, temp.Name, 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;
_sendViewModel.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();
string bucket = folderName;
if (string.IsNullOrWhiteSpace(folderName))
{
return;
}
if (!folderName.EndsWith("/"))
folderName += "/";
try
{
var bucketName = GetCurrentBucket();
var prefix = GetCurrentPrefix();
if (!string.IsNullOrEmpty(bucketName))
{
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"));
}
}
else
{
var mbArgs = new MakeBucketArgs().WithBucket(bucket);
await client.MakeBucketAsync(mbArgs);
}
// 刷新当前目录
_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 OnFileUploadCompleted(string currentPath)
{
// 检查当前路径是否匹配,如果是则更新显示
if (CurrentMinIOPath == currentPath || string.IsNullOrEmpty(currentPath))
{
Application.Current.Dispatcher.BeginInvoke(() =>
{
// 延迟一小段时间以确保服务器端数据同步完成
Task.Delay(1000).ContinueWith(_ =>
{
Application.Current.Dispatcher.Invoke(() =>
{
// 只触发Explorer控件的更新而不完全刷新整个页面
_explorerBehavior?.RefreshMinIOPath(CurrentMinIOPath);
// 更新状态信息
UpdateStatus("文件上传完成,已更新列表");
});
});
});
}
}
private void SelectItem(SystemInfoModel item)
{
if (item == null) return;
if (SelectedItems.Contains(item))
{
SelectedItems.Remove(item);
}
else
{
SelectedItems.Add(item);
}
UpdateStatus($"已选中{SelectedItems.Count} 个文件/文件夹");
}
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;
if (dirInfo.BucketName == dirInfo.FullName)
{
// 获取文件夹下所有对象
var listArgs = new ListObjectsArgs()
.WithBucket(bucketName);
List<string> list = new List<string>();
await foreach (var obj in client.ListObjectsEnumAsync(listArgs))
{
list.Add(obj.Key);
}
if (list.Count > 0)
{
SelectedItems.Clear();
throw new Exception($"仅能删除空桶!");
}
else
{
await client.RemoveBucketAsync(new RemoveBucketArgs().WithBucket(dirInfo.BucketName));
}
}
else
{
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
// 清理资源
public void Cleanup()
{
if (_sendViewModel != null)
{
_sendViewModel.FileUploadCompleted -= OnFileUploadCompleted;
}
}
}
internal class DataFileViewModel : ObservableSourceViewModel<TestViewModel>
{
protected override void Init()
{
base.Init();
}
}
}