464 lines
17 KiB
C#
464 lines
17 KiB
C#
using HeBianGu.Base.WpfBase;
|
|
using HeBianGu.Service.Mvc;
|
|
using Hopetry.Services;
|
|
using Microsoft.Win32;
|
|
using Minio.DataModel.Args;
|
|
using Minio.DataModel;
|
|
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.Windows;
|
|
using System.Windows.Input;
|
|
using System.Windows.Threading;
|
|
using Minio;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Hopetry.ViewModel.Send;
|
|
using System.IO;
|
|
using System.Security.AccessControl;
|
|
using System.Diagnostics;
|
|
using Microsoft.WindowsAPICodePack.Dialogs;
|
|
using Hopetry.Models;
|
|
using Yitter.IdGenerator;
|
|
|
|
namespace HeBianGu.App.Disk
|
|
{
|
|
[ViewModel("Loyout")]
|
|
internal class LoyoutViewModel : MvcViewModelBase
|
|
{
|
|
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 = "" });
|
|
// 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)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
#region 文件上传
|
|
private SendViewModel _sendViewModel;
|
|
private IConfiguration config;
|
|
private SemaphoreSlim _semaphore = new SemaphoreSlim(5);
|
|
private System.Timers.Timer _progressTimer;
|
|
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;
|
|
|
|
|
|
public ICommand UploadCommand { get; }
|
|
public ICommand UploadCommand1 { get; }
|
|
|
|
public LoyoutViewModel(SendViewModel sendViewModel)
|
|
{
|
|
_sendViewModel = sendViewModel;
|
|
_uploadService = new FileUploadService();
|
|
UploadCommand = new AsyncRelayCommand(async () => await UploadFile());
|
|
UploadCommand1 = new AsyncRelayCommand(async () => await UploadFile1());
|
|
// 初始化Timer
|
|
_progressTimer = new System.Timers.Timer(1000);
|
|
_progressTimer.Elapsed += UpdateProgress;
|
|
_sendViewModel.UpLoadItems.CollectionChanged += (s, e) =>
|
|
{
|
|
_sendViewModel.UpdateUploadingItems();
|
|
};
|
|
}
|
|
|
|
private void UpdateProgress(object sender, System.Timers.ElapsedEventArgs e)
|
|
{
|
|
lock (_timerLock)
|
|
{
|
|
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;
|
|
});
|
|
}
|
|
}
|
|
|
|
private void StartProgressTimer()
|
|
{
|
|
lock (_timerLock)
|
|
{
|
|
if (!_isTimerRunning)
|
|
{
|
|
_progressTimer.Start();
|
|
_isTimerRunning = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void StopProgressTimer()
|
|
{
|
|
lock (_timerLock)
|
|
{
|
|
if (_isTimerRunning)
|
|
{
|
|
_progressTimer.Stop();
|
|
_isTimerRunning = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task UploadFile()
|
|
{
|
|
var openFileDialog = new Microsoft.Win32.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;
|
|
}
|
|
|
|
foreach (string filePath in newFiles)
|
|
{
|
|
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();
|
|
_sendViewModel.FileCount = _completeCount + "/" + _uploadCount;
|
|
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;
|
|
}
|
|
|
|
foreach (string filePath in files)
|
|
{
|
|
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;
|
|
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();
|
|
|
|
try
|
|
{
|
|
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 (OperationCanceledException)
|
|
{
|
|
// 上传被取消
|
|
}
|
|
finally
|
|
{
|
|
await Task.Delay(2000);
|
|
StopProgressTimer();
|
|
_isUploading = false;
|
|
_sendViewModel.UpLoadItems.Clear();
|
|
if (MySetting.Instance.IsOn && !_sendViewModel.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();
|
|
// 从配置获取MinIO设置更安全
|
|
IMinioClient client = new Minio.MinioClient()
|
|
.WithEndpoint(config["Minio:Endpoint"])
|
|
.WithCredentials(config["Minio:AccessKey"], config["Minio:SecretKey"])
|
|
.Build();
|
|
|
|
try
|
|
{
|
|
|
|
string bucketName = config["Minio:BucketName"];
|
|
|
|
// 确保桶存在
|
|
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 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;
|
|
}
|
|
else
|
|
{
|
|
ut.Value3 = "上传中...";
|
|
}
|
|
});
|
|
});
|
|
|
|
var putObjectArgs = new PutObjectArgs()
|
|
.WithBucket(bucketName)
|
|
.WithObject(ut.Value6)
|
|
.WithFileName(ut.Value5)
|
|
.WithProgress(progress);
|
|
|
|
await client.PutObjectAsync(putObjectArgs);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Application.Current.Dispatcher.Invoke(() =>
|
|
{
|
|
ut.Value3 = $"上传失败: {ex.Message}";
|
|
});
|
|
}
|
|
}
|
|
|
|
private void Shutdown()
|
|
{
|
|
try
|
|
{
|
|
Process.Start("shutdown", "/s /t 0");
|
|
}
|
|
catch (System.ComponentModel.Win32Exception ex)
|
|
{
|
|
MessageBox.Show($"关机失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 文件列表
|
|
private string _currentMinIOPath;
|
|
/// <summary> 说明 </summary>
|
|
public string CurrentMinIOPath
|
|
{
|
|
get { return _currentMinIOPath; }
|
|
set
|
|
{
|
|
_currentMinIOPath = value;
|
|
RaisePropertyChanged("CurrentMinIOPath");
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
}
|
|
|
|
internal class DataFileViewModel : ObservableSourceViewModel<TestViewModel>
|
|
{
|
|
protected override void Init()
|
|
{
|
|
base.Init();
|
|
|
|
|
|
|
|
}
|
|
}
|
|
}
|