Compare commits

...

3 Commits

Author SHA1 Message Date
陈伟 40ecba881f Merge remote-tracking branch 'origin/dev2.0' into dev2.0 2025-04-12 16:24:21 +08:00
陈伟 c270561920 下载目录设置 2025-04-12 16:24:15 +08:00
陈伟 d29116d9aa 添加下载操作 2025-04-12 15:50:38 +08:00
5 changed files with 136 additions and 102 deletions

View File

@ -70,13 +70,14 @@ public class MinioDownloadTask : INotifyPropertyChanged
CancelCommand = new CustomCommand(OnCancel);
}
public MinioDownloadTask(MinioService minio, string bucket, string objectKey)
public MinioDownloadTask(MinioService minio, string bucket, string objectKey,string downDir)
{
Status = "等待中";
_minio = minio;
Bucket = bucket;
ObjectKey = objectKey;
FileName = Path.GetFileName(objectKey);
FilePath = "f:\\dest";
FilePath = downDir;
PauseCommand = new CustomCommand(OnPause);
CancelCommand = new CustomCommand(OnCancel);
}
@ -103,7 +104,7 @@ public class MinioDownloadTask : INotifyPropertyChanged
await client.Updateable(updateTask).IgnoreNullColumns().ExecuteCommandAsync();
}
Console.WriteLine($"{ObjectKey}文件下载中...");
Console.WriteLine($"id {TaskId} path: {FilePath} key: {ObjectKey}文件下载中...");
updateTask.Status = "下载中";
await _minio.DownLoadObject(Bucket, ObjectKey, FilePath, "");
Status = "已完成";

View File

@ -27,8 +27,8 @@
<Button h:Cattach.Icon="&#xe892;" Command="{Binding UploadCommand}" Content="多文件上传" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />
<Button h:Cattach.Icon="&#xe892;" Command="{Binding UploadCommand1}" Content="文件夹上传" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />
<!--<Button h:Cattach.Icon="&#xe891;" Content="下载" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />
<Button h:Cattach.Icon="&#xe763;" Content="分享" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />-->
<Button h:Cattach.Icon="&#xe891;" Command="{Binding DownloadCommand}" Content="下载" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />
<!--<Button h:Cattach.Icon="&#xe763;" Content="分享" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />-->
<Button h:Cattach.Icon="&#xe643;" Command="{Binding DeleteFolderCommand}" Content="删除" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />
<Button h:Cattach.Icon="&#xe688;" Command="{Binding CreateFolderCommand}" Content="新建文件夹" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />
<!--<Button h:Cattach.Icon="&#xe84f;" Content="离线下载" Style="{DynamicResource {x:Static h:ButtonKeys.Dynamic}}" />-->

View File

@ -1,4 +1,5 @@
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Timers;
@ -19,6 +20,7 @@ using Hopetry.ViewModel.Send;
using System.IO;
using System.Security.AccessControl;
using System.Diagnostics;
using System.Net.Http.Json;
using Microsoft.WindowsAPICodePack.Dialogs;
using Hopetry.Models;
using Yitter.IdGenerator;
@ -27,9 +29,11 @@ using static System.Windows.Forms.VisualStyles.VisualStyleElement.Window;
using HeBianGu.Control.Explorer;
using Hopetry.Provider.Behaviors;
using System.Text;
using Hopetry.Provider;
using Minio.ApiEndpoints;
using Minio.DataModel;
using Minio.DataModel.Args;
using Newtonsoft.Json;
using Timer = System.Timers.Timer;
using System.Collections.ObjectModel;
@ -40,6 +44,7 @@ namespace HeBianGu.App.Disk
{
private readonly FileUploadService _uploadService;
private string _path;
/// <summary> 说明 </summary>
public string Path
{
@ -53,6 +58,7 @@ namespace HeBianGu.App.Disk
private string _nearPath;
/// <summary> 说明 </summary>
public string NearPath
{
@ -66,6 +72,7 @@ namespace HeBianGu.App.Disk
private string _sharePath;
/// <summary> 说明 </summary>
public string SharePath
{
@ -78,7 +85,6 @@ namespace HeBianGu.App.Disk
}
protected override void Init()
{
Path = Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
@ -88,7 +94,8 @@ namespace HeBianGu.App.Disk
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 = "" });
LinkActions.Add(new LinkAction()
{ Action = "Explorer", Controller = "Loyout", DisplayName = "全部文件", Logo = "" });
// 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 = "" });
@ -99,21 +106,18 @@ namespace HeBianGu.App.Disk
//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];
}));
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
new Action(() => { SelectLink = LinkActions[0]; }));
}
protected override void Loaded(string args)
{
}
#region 文件上传
private SendViewModel _sendViewModel;
private IConfiguration config;
private SemaphoreSlim _semaphore = new SemaphoreSlim(5);
@ -129,8 +133,11 @@ namespace HeBianGu.App.Disk
public ICommand UploadCommand { get; }
public ICommand UploadCommand1 { get; }
public ICommand CreateFolderCommand { get; }
public ICommand DeleteFolderCommand { get; }
public ICommand CreateFolderCommand { get; }
public ICommand DownloadCommand { get; set; }
public ICommand DeleteFolderCommand { get; }
//public ICommand SelectItemsCommand { get; }
public LoyoutViewModel(SendViewModel sendViewModel)
{
@ -140,20 +147,44 @@ namespace HeBianGu.App.Disk
UploadCommand1 = new AsyncRelayCommand(async () => await UploadFile1());
CreateFolderCommand = new AsyncRelayCommand(async () => await CreateNewFolderAsync());
DeleteFolderCommand = new AsyncRelayCommand(async () => await DeleteSelectedItemsAsync());
DownloadCommand = new AsyncRelayCommand(async () => await DoDownloadCommand());
//SelectItemsCommand = new AsyncRelayCommand(async () => await RelayCommand<SystemInfoModel>(SelectItem)SelectItem());
// 初始化Timer
_progressTimer = new Timer(1000);
_progressTimer.Elapsed += UpdateProgress;
_sendViewModel.UpLoadItems.CollectionChanged += (s, e) =>
{
_sendViewModel.UpdateUploadingItems();
};
_sendViewModel.UpLoadItems.CollectionChanged += (s, e) => { _sendViewModel.UpdateUploadingItems(); };
}
private async Task DoDownloadCommand()
{
// todo 当为文件时,处理
var selectedItems = _selectedItems;
// 清除选中
//SelectedItems = [];
foreach (var item in selectedItems)
{
if (item is MinIOFileModel file)
{
var temp = (MinIOFileInfo)file.Model;
var objectKey = temp.FullName.Replace(temp.BucketName + "/", "");
ViewModelLocator.DownViewModel.AddTask(temp.BucketName, objectKey);
}
if (item is MinIODirectoryModel dir)
{
Console.WriteLine("建设中...");
//return ((MinIODirectoryInfo)dir.Model).BucketName;
}
}
}
// 添加设置Behavior的方法
public void SetExplorerBehavior(ExplorerMinIOBehavior behavior)
{
_explorerBehavior = behavior;
}
private void UpdateProgress(object sender, System.Timers.ElapsedEventArgs e)
{
lock (_timerLock)
@ -164,10 +195,7 @@ namespace HeBianGu.App.Disk
double totalBytes = _sendViewModel.UpLoadItems.Sum(r => r.Double1);
double progress = Math.Round((currentBytes / totalBytes) * 100, 2);
Application.Current.Dispatcher.Invoke(() =>
{
_sendViewModel.Progress = progress;
});
Application.Current.Dispatcher.Invoke(() => { _sendViewModel.Progress = progress; });
}
}
@ -222,6 +250,7 @@ namespace HeBianGu.App.Disk
var ut = CreateUploadItem(filePath, System.IO.Path.GetFileName(filePath));
_sendViewModel.UpLoadItems.Add(ut);
}
_uploadCount = _sendViewModel.UpLoadItems.Count;
//_uploadCount = _sendViewModel.UpLoadItems.Count;
_completeCount = _sendViewModel.UpLoadItems.Where(r => r.Value3 == "已完成").Count();
@ -260,10 +289,12 @@ namespace HeBianGu.App.Disk
foreach (string filePath in files)
{
string relativePath = folderName + "/" + filePath.Substring(folderPath.Length + 1).Replace('\\', '/');
string relativePath =
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;
@ -280,27 +311,27 @@ namespace HeBianGu.App.Disk
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";
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;
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.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, //唯一标识,与数据库一致
Value4 = fp.Id, //唯一标识,与数据库一致
Value5 = filePath,
Value6 = objectName,
Double1 = fileInfo.Length,
@ -370,8 +401,8 @@ namespace HeBianGu.App.Disk
{
ut.Bool1 = true;
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
// 构建配置
var config = builder.Build();
// 从配置获取MinIO设置更安全
@ -379,10 +410,9 @@ namespace HeBianGu.App.Disk
.WithEndpoint(config["Minio:Endpoint"])
.WithCredentials(config["Minio:AccessKey"], config["Minio:SecretKey"])
.Build();
try
{
string bucketName = config["Minio:BucketName"];
// 确保桶存在
@ -404,9 +434,9 @@ namespace HeBianGu.App.Disk
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";
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)
@ -418,7 +448,7 @@ namespace HeBianGu.App.Disk
ut.Value3 = "已完成";
_sendViewModel.UpdateUploadingItems();
_completeCount++;
_sendViewModel.FileCount=_completeCount+"/"+_uploadCount;
_sendViewModel.FileCount = _completeCount + "/" + _uploadCount;
}
else
{
@ -433,14 +463,11 @@ namespace HeBianGu.App.Disk
.WithFileName(ut.Value5)
.WithProgress(progress);
await client.PutObjectAsync(putObjectArgs);
await client.PutObjectAsync(putObjectArgs);
}
catch (Exception ex)
{
Application.Current.Dispatcher.Invoke(() =>
{
ut.Value3 = $"上传失败: {ex.Message}";
});
Application.Current.Dispatcher.Invoke(() => { ut.Value3 = $"上传失败: {ex.Message}"; });
}
}
@ -455,10 +482,13 @@ namespace HeBianGu.App.Disk
MessageBox.Show($"关机失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region 文件列表
private string _currentMinIOPath;
/// <summary> 说明CurrentMinIOPath </summary>
public string CurrentMinIOPath
{
@ -488,7 +518,6 @@ namespace HeBianGu.App.Disk
}
public ObservableCollection<SystemInfoModel> Items { get; } = new ObservableCollection<SystemInfoModel>();
private async Task CreateNewFolderAsync()
@ -523,8 +552,8 @@ namespace HeBianGu.App.Disk
public async Task CreateMinIOFolder(string folderName)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("global.json", optional: false, reloadOnChange: true);
// 构建配置
var config = builder.Build();
// 从配置获取MinIO设置更安全
@ -559,7 +588,7 @@ namespace HeBianGu.App.Disk
.WithBucket(bucketName)
.WithObject(prefix + folderName)
.WithStreamData(emptyStream)
.WithObjectSize(1) // 必须设置为0
.WithObjectSize(1) // 必须设置为0
.WithContentType("application/x-directory"));
}
@ -587,18 +616,27 @@ namespace HeBianGu.App.Disk
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(); }
set
{
_selectedItems = value;
RaisePropertyChanged();
}
}
private ICommand _selectItemsCommand;
public ICommand SelectItemsCommand => _selectItemsCommand ?? (_selectItemsCommand = new RelayCommand<SystemInfoModel>(SelectItem));
public ICommand SelectItemsCommand => _selectItemsCommand ??
(_selectItemsCommand = new RelayCommand<SystemInfoModel>(SelectItem));
private void SelectItem(SystemInfoModel item)
{
@ -613,6 +651,7 @@ namespace HeBianGu.App.Disk
SelectedItems.Add(item);
}
}
private async Task DeleteSelectedItemsAsync()
{
try
@ -712,7 +751,6 @@ namespace HeBianGu.App.Disk
}
#endregion
}
internal class DataFileViewModel : ObservableSourceViewModel<TestViewModel>
@ -720,7 +758,6 @@ namespace HeBianGu.App.Disk
protected override void Init()
{
base.Init();
}
}
}
}

View File

@ -2,7 +2,6 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows.Data;
using System.Windows.Input;
using HeBianGu.Base.WpfBase;
@ -11,7 +10,6 @@ using Hopetry.Models;
using Hopetry.Provider;
using Hopetry.Services;
using Newtonsoft.Json;
using SqlSugar;
namespace Hopetry.ViewModel.Send;
@ -22,13 +20,12 @@ public class DownViewModel : MvcViewModelBase
public RelayCommand<MinioDownloadTask> OpenDownItemFolder { get; set; }
private readonly ConcurrentQueue<MinioDownloadTask> _taskQueue = new();
private SemaphoreSlim _concurrencySemaphore;
private SemaphoreSlim _semaphore;
private CancellationTokenSource _processingCts = new();
public int count { get; set; }
private SemaphoreSlim _semaphore;
private readonly ReaderWriterLockSlim _lock = new();
/// <summary>
/// 全部任务动态标题
@ -70,7 +67,7 @@ public class DownViewModel : MvcViewModelBase
}
// 绑定属性
public ObservableCollection<MinioDownloadTask> AllTasks { get; set; } = new();
public ObservableCollection<MinioDownloadTask> AllTasks { get; set; }
public ICollectionView RunningTasksView { get; set; }
@ -106,27 +103,23 @@ public class DownViewModel : MvcViewModelBase
// 命令
public ICommand AddTaskCommand { get; set; }
public ICommand ClearFinishedCommand { get; }
private readonly ISerializerService _serializerService;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="minioService"></param>
public DownViewModel(MinioService minioService, SemaphoreSlim semaphore)
public DownViewModel(MinioService minioService, ISerializerService serializerService)
{
_serializerService = serializerService;
_minioService = minioService;
_semaphore = semaphore;
Console.WriteLine("初始化DownViewModel");
using var client = SqlSugarConfig.GetSqlSugarScope();
var data = client.Ado.SqlQuery<MinioDownloadTask>($"select * from download_task");
//DownItems = new ObservableCollection<DownItem>(data);
AllTasks = new ObservableCollection<MinioDownloadTask>(data);
AllTaskHeader = $"全部({AllTasks.Count}";
//Console.WriteLine(JsonConvert.SerializeObject(data));
OpenDownItemFolder = new RelayCommand<MinioDownloadTask>(DoOpenDownItemFolder);
// 命令初始化
// AddTaskCommand = new RelayCommand(AddTask, CanAddTask);
// 修正:使用 CollectionViewSource 确保通知
var cvsRunning = new CollectionViewSource { Source = AllTasks };
cvsRunning.Filter += (s, e) =>
{
@ -142,15 +135,7 @@ public class DownViewModel : MvcViewModelBase
e.Accepted = b;
};
FinishedTasksView = cvsFinished.View;
if (FinishedTasksView is ListCollectionView finishListView)
{
FinishedTaskHeader = $"已完成({finishListView.Count}";
}
if (RunningTasksView is ListCollectionView runningListView)
{
RunningTaskHeader = $"下载中({runningListView.Count}";
}
RefreshViewHeader();
// 关键:订阅数据源变更事件,强制视图刷新
AllTasks.CollectionChanged += (s, e) =>
@ -166,41 +151,50 @@ public class DownViewModel : MvcViewModelBase
// 启动任务处理线程
_semaphore = new SemaphoreSlim(_maxConcurrent);
// 启动任务处理线程
_concurrencySemaphore = new SemaphoreSlim(_maxConcurrent);
new Thread(ProcessTasksLoop) { IsBackground = true }.Start();
AddTaskCommand = new ActionCommand(AddTask);
//AddTaskCommand = new ActionCommand(AddTask);
}
private void ProcessTasksLoop()
private void RefreshViewHeader()
{
if (FinishedTasksView is ListCollectionView finishListView)
{
FinishedTaskHeader = $"已完成({finishListView.Count}";
}
if (RunningTasksView is ListCollectionView runningListView)
{
RunningTaskHeader = $"下载中({runningListView.Count}";
}
}
private async void ProcessTasksLoop()
{
int c1 = 1;
while (!_processingCts.IsCancellationRequested)
{
_semaphore.WaitAsync();
// 按当前并发数启动任务
for (int i = 0; i < _maxConcurrent; i++)
await _semaphore.WaitAsync();
if (_taskQueue.TryDequeue(out var task))
{
if (_taskQueue.TryDequeue(out var task))
{
// 启动新线程执行下载任务
_ = Task.Run(() => ExecuteTaskAsync(task));
}
Console.WriteLine($"我进入的异常下载{c1++}");
// 启动新线程执行下载任务
// _ = Task.Run(() => ExecuteTaskAsync(task));
_ = ExecuteTaskAsync(task).ContinueWith(_ => _semaphore.Release());
}
Thread.Sleep(100);
}
}
private void AddTask()
public void AddTask(string bucketName, string objectKey)
{
string[] objectKeys = { "demo/06.mp4", "demo/07.mp4", "demo/08.mp4", "demo/10.mp4" };
var task = new MinioDownloadTask(_minioService, "demo", objectKeys[count++]);
var downDir = ViewModelLocator.SyncViewModel.SyncDir;
var task = new MinioDownloadTask(_minioService, bucketName, objectKey,downDir);
using var client = SqlSugarConfig.GetSqlSugarScope();
client.Insertable(task).ExecuteCommandIdentityIntoEntity();
AllTasks.Add(task);
_taskQueue.Enqueue(task);
RefreshViewHeader();
}
@ -209,25 +203,27 @@ public class DownViewModel : MvcViewModelBase
try
{
await task.StartDownload();
// todo 任务完成下载
var x = AllTasks.IndexOf(task);
Console.WriteLine($"完成任务在全部任务中的位置 {x}");
RefreshViewHeader();
Console.WriteLine($"异步下载完成:{task.FileName}");
}
finally
{
_semaphore.Release();
}
}
private void AdjustConcurrency()
{
lock (_concurrencySemaphore)
lock (_semaphore)
{
// 调整信号量容量
var delta = _maxConcurrent - _concurrencySemaphore.CurrentCount;
var delta = _maxConcurrent - _semaphore.CurrentCount;
if (delta > 0)
{
for (int i = 0; i < delta; i++)
{
_concurrencySemaphore.Release();
_semaphore.Release();
}
}
}

BIN
minio.db

Binary file not shown.