Compare commits

...

3 Commits

Author SHA1 Message Date
陈伟 b42ea1f55c Merge remote-tracking branch 'origin/dev2.0' into dev2.0
# Conflicts:
#	ViewModel/Loyout/LoyoutViewModel.cs
#	minio.db
2025-04-24 16:48:06 +08:00
陈伟 f1cd0eabe5 文件夹下载支持(有bug) 2025-04-24 16:44:43 +08:00
陈伟 cc1f538208 1. 全部暂停,可能会有一个不被暂停问题
2. 进度除0报错问题
2025-04-23 16:34:24 +08:00
6 changed files with 221 additions and 57 deletions

View File

@ -272,6 +272,7 @@ namespace HeBianGu.App.Disk
x.Settings.Clear();
x.Settings.Add(MySetting.Instance);
x.Settings.Add(SyncSetting.Instance);
x.Settings.Add(DownloadSetting.Instance);
});
app.UseStyle();
//app.UseSettingDefault();
@ -296,6 +297,40 @@ namespace HeBianGu.App.Disk
}
}
[Displayer(Name = "下载设置", GroupName = "通用设置")]
public class DownloadSetting : LazySettingInstance<DownloadSetting>
{
private string _downDir;
[Display(Name = "下载目录")]
public string DownDir
{
get => _downDir;
set
{
if (_downDir == value) return;
_downDir = value;
RaisePropertyChanged();
}
}
private bool _isSelect;
[DefaultValue(false)]
[Display(Name = "设置为默认下载目录")]
public bool IsSelect
{
get => _isSelect;
set
{
if (_isSelect == value) return;
_isSelect = value;
RaisePropertyChanged();
}
}
}
[Displayer(Name = "同步设置", GroupName = "通用设置")]
public class SyncSetting : LazySettingInstance<SyncSetting>

View File

@ -31,7 +31,7 @@ public class MinioDownloadTask : NotifyPropertyChangedBase
public string FileName { get; set; }
[SugarColumn(ColumnName = "bucket_name")]
public string Bucket { get; set; }
public string BucketName { get; set; }
[SugarColumn(ColumnName = "object_key")]
public string ObjectKey { get; set; }
@ -59,10 +59,22 @@ public class MinioDownloadTask : NotifyPropertyChangedBase
[SugarColumn(ColumnName = "file_etag")]
public string FileETag { get; set; }
private String _downloadInfo;
/// <summary>
/// 0 普通对象 1 文件夹 2 存储桶
/// </summary>
[SugarColumn(ColumnName = "type")]
public int Type { get; set; }
[SugarColumn(ColumnName = "file_number")]
public int FileNumber { get; set; }
[SugarColumn(ColumnName = "download_number")]
public int DownloadNumber { get; set; }
private string _downloadInfo;
[SugarColumn(IsIgnore = true)]
public String DownloadInfo
public string DownloadInfo
{
get => _downloadInfo;
set
@ -94,6 +106,11 @@ public class MinioDownloadTask : NotifyPropertyChangedBase
{
if (_progress == 0)
{
if (TotalSize == 0)
{
return 0d;
}
_progress = Downloaded == 0 ? 0 : (Downloaded * 100 / TotalSize);
}
@ -126,17 +143,17 @@ public class MinioDownloadTask : NotifyPropertyChangedBase
Status = "等待中";
}
public MinioDownloadTask(MinioService minio, string bucket, string objectKey, string downDir, string fileSize)
public MinioDownloadTask(MinioService minio, string bucketName, string objectKey, string downDir, string fileSize)
{
StartOrPauseIcon = "\xe76e";
Status = "等待中";
Minio = minio;
Bucket = bucket;
BucketName = bucketName;
ObjectKey = objectKey;
FileName = Path.GetFileName(objectKey);
FilePath = downDir;
CreateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
var x = minio.GetObjectMetadata(bucket, objectKey);
var x = minio.GetObjectMetadata(bucketName, objectKey);
TotalSize = x.Result.Size;
FileSize = fileSize;
Speed = Status;
@ -153,7 +170,7 @@ public class MinioDownloadTask : NotifyPropertyChangedBase
StopDownTs = new CancellationTokenSource();
try
{
var stat = await Minio.GetObjectMetadata(Bucket, ObjectKey);
var stat = await Minio.GetObjectMetadata(BucketName, ObjectKey);
// 获取对象信息
TotalSize = stat.Size;
FileETag = stat.ETag;
@ -178,7 +195,7 @@ public class MinioDownloadTask : NotifyPropertyChangedBase
: $"{TotalSize / 1024}KB");
var offset = Downloaded;
Console.WriteLine("偏移量:" + (offset > 1048576 ? $"{offset / 1048576:f2}MB" : $"{(offset) / 1024:f2}KB"));
await Minio.DownLoadObjectWithCallBack(this, Bucket, ObjectKey, FilePath, FileETag,
await Minio.DownLoadObjectWithCallBack(this, BucketName, ObjectKey, FilePath, FileETag,
(downloaded, total, speed) =>
{
var progress = (double)(downloaded + offset) / total * 100;
@ -217,4 +234,38 @@ public class MinioDownloadTask : NotifyPropertyChangedBase
RaisePropertyChanged(nameof(Status));
}
}
public async Task StartDownloadDir()
{
StopDownTs = new CancellationTokenSource();
// 改变任务状态
Status = "下载中";
var updateTask = new MinioDownloadTask
{
TaskId = TaskId
};
var prefix = BucketName.Equals(ObjectKey) ? "" : ObjectKey;
var allObject = await Minio.ListAllObject(BucketName, prefix, true, StopDownTs.Token);
await foreach (var item in allObject)
{
// 验证文件是否存在
if (File.Exists(Path.Combine(FilePath, item.Key))) continue;
var localDir = Type == 1 ? FilePath : Path.Combine(FilePath, BucketName);
await Minio.DownLoadObject(BucketName, item.Key, localDir, item.ETag);
DownloadNumber++;
DownloadInfo = $"{DownloadNumber}/{FileNumber}";
Speed = "下载中";
Progress = (double)DownloadNumber / FileNumber * 100;
using var xxx = SqlSugarConfig.GetSqlSugarScope();
updateTask.DownloadNumber = DownloadNumber;
await xxx.Updateable(updateTask).IgnoreNullColumns().ExecuteCommandAsync();
}
Status = "已完成";
updateTask.Status = Status;
updateTask.FinishedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
updateTask.DownloadNumber = DownloadNumber;
using var client = SqlSugarConfig.GetSqlSugarScope();
await client.Updateable(updateTask).IgnoreNullColumns().ExecuteCommandAsync();
}
}

View File

@ -5,6 +5,7 @@ using System.Threading.Channels;
using Hopetry.Provider;
using Microsoft.Extensions.Configuration;
using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Minio.DataModel.Notification;
using Minio.Exceptions;
@ -232,41 +233,36 @@ namespace Hopetry.Services
/// <summary>
/// 列出存储桶内所有文件
/// </summary>
/// <param name="bucket"></param>
public async Task ListAllObject()
/// <param name="bucketName"></param>
/// <param name="prefix"></param>
/// <param name="recursive"></param>
public async Task<IAsyncEnumerable<Item>> ListAllObject(string bucketName, string prefix, bool recursive,
CancellationToken token = default)
{
// Just list of objects
// Check whether 'mybucket' exists or not.
var existsArgs = new BucketExistsArgs().WithBucket(_bucketName);
bool found = await _minioClient.BucketExistsAsync(existsArgs);
if (string.IsNullOrEmpty(bucketName))
{
bucketName = _bucketName;
}
var existsArgs = new BucketExistsArgs().WithBucket(bucketName);
var found = await _minioClient.BucketExistsAsync(existsArgs, token);
if (found)
{
// List objects from 'my-bucketname'
ListObjectsArgs args = new ListObjectsArgs()
.WithBucket(_bucketName)
.WithRecursive(false);
// ListObjectsEnumAsync 新方法
var observable = _minioClient.ListObjectsEnumAsync(args);
var args = new ListObjectsArgs()
.WithBucket(bucketName)
.WithPrefix(prefix)
.WithRecursive(recursive);
return _minioClient.ListObjectsEnumAsync(args, token);
}
var x = observable.GetAsyncEnumerator();
await x.MoveNextAsync();
Console.WriteLine(x.Current.Key);
Console.WriteLine(x.Current.Size);
Console.WriteLine(x.Current.ETag);
Console.WriteLine(x.Current.LastModified);
Console.WriteLine(x.Current.IsDir);
/*IDisposable subscription = observable.Subscribe(
item => Console.WriteLine("OnNext: {0}", item.Key),
ex => Console.WriteLine("OnError: {0}", ex.Message),
() => Console.WriteLine("OnComplete: {0}"));*/
}
else
{
Console.WriteLine("mybucket does not exist");
}
Console.WriteLine("mybucket does not exist");
throw new Exception("bucket not found");
}
public async Task DownLoadObject(string bucketName, string objectKey, string localDir, string objectETag)
public async Task DownLoadObject(string bucketName, string objectKey, string localDir, string objectETag,
CancellationToken token = default)
{
var index = objectKey.LastIndexOf("/", StringComparison.Ordinal);
if (index > 0)
@ -283,7 +279,7 @@ namespace Hopetry.Services
.WithBucket(string.IsNullOrEmpty(bucketName) ? _bucketName : bucketName)
.WithObject(objectKey)
.WithFile(localPath);
var stat = await _minioClient.GetObjectAsync(getArgs);
var stat = await _minioClient.GetObjectAsync(getArgs, token);
if (VerifyETag(localPath, objectETag))
{
// todo 先忽略处理

View File

@ -618,7 +618,7 @@ namespace HeBianGu.App.Disk
private async Task DoDownloadCommand()
{
// todo 当为文件时,处理
// 当为文件时,处理
var tempSelectedItems = new ObservableCollection<SystemInfoModel>(SelectedItems);
if (tempSelectedItems.Count > 0)
{
@ -628,7 +628,7 @@ namespace HeBianGu.App.Disk
DownFolderSelect.DownFolder = ViewModelLocator.SyncViewModel.SyncDir;
}
// todo 假如设置了默认下载目录,开启下载
//下载目录
bool r = await MessageProxy.Presenter.Show(DownFolderSelect, x => true, "下载目录选择", x =>
{
x.Width = 600;
@ -656,7 +656,7 @@ namespace HeBianGu.App.Disk
}
}
private void AddTaskToDownLoad(ObservableCollection<SystemInfoModel> tempSelectedItems, string downDir)
private async void AddTaskToDownLoad(ObservableCollection<SystemInfoModel> tempSelectedItems, string downDir)
{
foreach (var item in tempSelectedItems)
{
@ -664,15 +664,19 @@ namespace HeBianGu.App.Disk
{
var temp = (MinIOFileInfo)file.Model;
var objectKey = temp.FullName.Replace(temp.BucketName + "/", "");
// todo 当对象key不存在时
ViewModelLocator.DownViewModel.AddTask(temp.BucketName, objectKey, temp.Size, downDir);
}
else if (item is MinIODirectoryModel dir)
{
Console.WriteLine("建设中...");
MessageProxy.Snacker.Show("当前不支持下载文件夹");
//return ((MinIODirectoryInfo)dir.Model).BucketName;
// todo 支持存储桶下载 判断要下载对象存不存在 存储桶存不存在 存储桶下对象存不存在
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("当前不支持下载文件夹");
}
MessageProxy.Snacker.Show("已加入下载队列");
}
}

View File

@ -29,6 +29,7 @@ public class DownViewModel : MvcViewModelBase
public int count { get; set; }
private readonly ReaderWriterLockSlim _lock = new();
// 下载中 功能按钮显示
private Visibility _tab0Visibility;
public Visibility Tab0Visibility
@ -55,6 +56,7 @@ public class DownViewModel : MvcViewModelBase
private int _tabIndex;
// tab 索引
public int TabIndex
{
get => _tabIndex;
@ -181,7 +183,9 @@ public class DownViewModel : MvcViewModelBase
/// <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");
@ -193,9 +197,11 @@ public class DownViewModel : MvcViewModelBase
StartOrPauseDown = new RelayCommand<MinioDownloadTask>(DoStartOrPauseDown);
// 全部暂停
PauseAllTask = new CustomCommand(DoPauseAllTask);
StopAllTask = new CustomCommand(DoStopAllTask);
// 全部取消
StopAllTask = new CustomCommand(DoStopAllTask);
// 加载已完成任务记录
LoadFinishedTasks();
// 加载未完成任务
LoadRunningTasks();
ClearFinishedCommand = new CustomCommand(DoClearFinishedCommand);
// 启动任务处理线程
@ -216,6 +222,7 @@ public class DownViewModel : MvcViewModelBase
{
minioDownloadTask.StopDownTs.Cancel();
}
minioDownloadTask.Status = "删除中";
using var client = SqlSugarConfig.GetSqlSugarScope();
client.Deleteable<MinioDownloadTask>().Where(x => x.TaskId == minioDownloadTask.TaskId).ExecuteCommand();
@ -231,36 +238,36 @@ public class DownViewModel : MvcViewModelBase
{
foreach (var minioDownloadTask in RunningTasks)
{
// 清空下载队列
_taskQueue.Clear();
//暂停下载
if (minioDownloadTask.StopDownTs != null)
{
minioDownloadTask.StopDownTs.Cancel();
}
// 清空下载队列
_taskQueue.Clear();
minioDownloadTask.Status = "已暂停";
minioDownloadTask.StartOrPauseIcon = "\xe748";
// 速度及剩余时间(视图显示信息)
minioDownloadTask.Speed = "已暂停";
}
}
private void DoStartOrPauseDown(MinioDownloadTask item)
{
if (item.Status == "下载中" || item.Status == "等待中")
if (item.Status is "下载中" or "等待中")
{
if (item.StopDownTs != null)
{
//暂停下载
item.StopDownTs.Cancel();
}
item.Status = "已暂停";
item.StartOrPauseIcon = "\xe748";
// 速度及剩余时间(视图显示信息)
item.Speed = "已暂停";
// 避免结构函数加载的任务报错
}
else if (item.Status == "已暂停")
{
@ -300,7 +307,7 @@ public class DownViewModel : MvcViewModelBase
Status = "已暂停",
TaskId = x.TaskId,
FileName = x.FileName,
Bucket = x.Bucket,
BucketName = x.BucketName,
ObjectKey = x.ObjectKey,
TotalSize = x.TotalSize,
Downloaded = x.Downloaded,
@ -309,10 +316,14 @@ public class DownViewModel : MvcViewModelBase
FinishedTime = x.FinishedTime,
FileSize = x.FileSize,
FileETag = x.FileETag,
FileNumber = x.FileNumber,
DownloadNumber = x.DownloadNumber,
Type = x.Type,
// 初始化显示
DownloadInfo =
(x.Downloaded > 1048576 ? $"{x.Downloaded / 1048576:f2}MB" : $"{x.Downloaded / 1024:f2}KB") +
$"/{x.FileSize}",
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();
@ -383,7 +394,15 @@ public class DownViewModel : MvcViewModelBase
task.Minio = _minioService;
}
await task.StartDownload();
if (task.Type == 0)
{
await task.StartDownload();
}
else
{
await task.StartDownloadDir();
}
Application.Current.Dispatcher.Invoke(() =>
{
RunningTasks.Remove(task);
@ -428,19 +447,33 @@ public class DownViewModel : MvcViewModelBase
{
//Console.WriteLine($"点击item值{JsonConvert.SerializeObject(para)}");
//Process.Start("explorer.exe", para.FilePath);
var file = Path.Combine(para.FilePath, para.FileName);
Process.Start("explorer.exe", $"/select,\"{file}\"");
if (para.Type == 0)
{
var file = Path.Combine(para.FilePath, para.FileName);
Process.Start("explorer.exe", $"/select,\"{file}\"");
}
else if (para.Type == 1)
{
var combinePath = Path.Combine(para.FilePath, para.ObjectKey);
combinePath = combinePath.Replace("/", "\\");
Process.Start("explorer.exe", combinePath);
}
else
{
Process.Start("explorer.exe", Path.Combine(para.FilePath, para.BucketName));
}
}
public void DoCancelDownload(MinioDownloadTask item)
{
Console.WriteLine("取消下载");
// 取消操作
if (item.StopDownTs != null)
{
item.StopDownTs.Cancel();
}
item.Status = "删除中";
using var client = SqlSugarConfig.GetSqlSugarScope();
client.Deleteable<MinioDownloadTask>().Where(x => x.TaskId == item.TaskId).ExecuteCommand();
@ -459,4 +492,49 @@ public class DownViewModel : MvcViewModelBase
protected override void Loaded(string args)
{
}
public async void AddDirTask(string bucketName, string objectKey, string downDir)
{
// 情景1 只选了一个文件夹
// 情景2 只选了一个存储桶
// 嵌套情景: 文件夹或者存储桶下,存在文件夹
var task = new MinioDownloadTask
{
FilePath = downDir,
ObjectKey = objectKey,
BucketName = bucketName,
FileName = objectKey,
Status = "等待中",
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.DownloadInfo = $"0/{number}";
using var client = SqlSugarConfig.GetSqlSugarScope();
await client.Insertable(task).ExecuteCommandIdentityIntoEntityAsync();
Application.Current.Dispatcher.Invoke(() =>
{
RunningTasks.Add(task);
RefreshHeader();
});
_taskQueue.Enqueue(task);
}
}

BIN
minio.db

Binary file not shown.