Files
community/Ink Canvas/Windows/SettingsViews/Pages/StoragePage.xaml.cs
T
2026-05-01 20:38:59 +08:00

330 lines
13 KiB
C#

using Ink_Canvas.Helpers;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using MessageBox = iNKORE.UI.WPF.Modern.Controls.MessageBox;
using Page = iNKORE.UI.WPF.Modern.Controls.Page;
namespace Ink_Canvas.Windows.SettingsViews.Pages
{
public partial class StoragePage : Page
{
// 已知子目录定义。其余顶层文件归入“核心文件”或“其他”。
private static readonly string[] LogDirs = { "Logs", "Crashs" };
private static readonly string[] InkDirs = { "Saves" };
private static readonly string[] BackupDirs = { "Backups" };
private static readonly string[] CustomDirs = { "icons", "backgrounds" };
private static readonly string[] PluginDirs = { "plugins", "Plugins" };
private static readonly string[] UpdateDirs = { "AutoUpdate" };
private static readonly string[] ConfigDirs = { "Configs" };
// 视为核心文件的扩展名(位于应用根目录下)
private static readonly string[] CoreFileExtensions =
{
".exe", ".dll", ".json", ".enc", ".dat", ".config", ".pdb",
".xml", ".manifest", ".runtimeconfig.json", ".deps.json"
};
private long _coreSize, _logsSize, _inkSize, _backupsSize,
_customSize, _pluginsSize, _updateSize, _otherSize;
public StoragePage()
{
InitializeComponent();
Loaded += StoragePage_Loaded;
}
private async void StoragePage_Loaded(object sender, RoutedEventArgs e)
{
await RefreshAsync();
}
private void BtnRefresh_Click(object sender, RoutedEventArgs e)
{
_ = RefreshAsync();
}
private void BtnOpenAppFolder_Click(object sender, RoutedEventArgs e)
{
try
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
{
FileName = App.RootPath,
UseShellExecute = true
});
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"打开应用目录失败: {ex.Message}", LogHelper.LogType.Error);
}
}
private async Task RefreshAsync()
{
SetAllSizesText(LocalizationHelper.GetString("Storage_Calculating"));
try
{
await Task.Run(() => CalculateSizes());
UpdateUI();
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"计算存储占用失败: {ex.Message}", LogHelper.LogType.Error);
SetAllSizesText(LocalizationHelper.GetString("Storage_CalculateFailed"));
}
}
private void CalculateSizes()
{
_coreSize = _logsSize = _inkSize = _backupsSize =
_customSize = _pluginsSize = _updateSize = _otherSize = 0;
string root = App.RootPath;
if (string.IsNullOrEmpty(root) || !Directory.Exists(root)) return;
// 顶层目录归类
foreach (var dir in SafeEnumerateDirectories(root))
{
string name = Path.GetFileName(dir);
long size = GetDirectorySize(dir);
if (LogDirs.Any(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase)))
_logsSize += size;
else if (InkDirs.Any(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase)))
_inkSize += size;
else if (BackupDirs.Any(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase)))
_backupsSize += size;
else if (CustomDirs.Any(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase)))
_customSize += size;
else if (PluginDirs.Any(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase)))
_pluginsSize += size;
else if (UpdateDirs.Any(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase)))
_updateSize += size;
else if (ConfigDirs.Any(n => string.Equals(n, name, StringComparison.OrdinalIgnoreCase)))
_coreSize += size;
else
_otherSize += size;
}
// 顶层文件按扩展名归类
foreach (var file in SafeEnumerateFiles(root))
{
long size = SafeFileLength(file);
string ext = Path.GetExtension(file).ToLowerInvariant();
if (CoreFileExtensions.Contains(ext))
_coreSize += size;
else
_otherSize += size;
}
}
private void UpdateUI()
{
long total = _coreSize + _logsSize + _inkSize + _backupsSize
+ _customSize + _pluginsSize + _updateSize + _otherSize;
TotalSizeTextBlock.Text = FormatSize(total);
CoreSizeText.Text = FormatSize(_coreSize);
LogsSizeText.Text = FormatSize(_logsSize);
InkSizeText.Text = FormatSize(_inkSize);
BackupsSizeText.Text = FormatSize(_backupsSize);
CustomSizeText.Text = FormatSize(_customSize);
PluginsSizeText.Text = FormatSize(_pluginsSize);
UpdateSizeText.Text = FormatSize(_updateSize);
OtherSizeText.Text = FormatSize(_otherSize);
// 更新柱状图列宽
if (total > 0)
{
BarCoreCol.Width = new GridLength(_coreSize, GridUnitType.Star);
BarLogsCol.Width = new GridLength(_logsSize, GridUnitType.Star);
BarInkCol.Width = new GridLength(_inkSize, GridUnitType.Star);
BarBackupsCol.Width = new GridLength(_backupsSize, GridUnitType.Star);
BarCustomCol.Width = new GridLength(_customSize, GridUnitType.Star);
BarPluginsCol.Width = new GridLength(_pluginsSize, GridUnitType.Star);
BarUpdateCol.Width = new GridLength(_updateSize, GridUnitType.Star);
BarOtherCol.Width = new GridLength(_otherSize, GridUnitType.Star);
}
else
{
BarCoreCol.Width = BarLogsCol.Width = BarInkCol.Width =
BarBackupsCol.Width = BarCustomCol.Width =
BarPluginsCol.Width = BarUpdateCol.Width =
BarOtherCol.Width = new GridLength(0, GridUnitType.Star);
}
// 占磁盘比例
try
{
var driveLetter = Path.GetPathRoot(App.RootPath);
if (!string.IsNullOrEmpty(driveLetter))
{
var drive = new DriveInfo(driveLetter);
if (drive.IsReady && drive.TotalSize > 0)
{
double pct = (double)total / drive.TotalSize * 100.0;
DiskPercentTextBlock.Text = pct < 0.01 ? "<0.01 %" : $"{pct:0.##} %";
return;
}
}
}
catch { }
DiskPercentTextBlock.Text = "—";
}
private void SetAllSizesText(string text)
{
TotalSizeTextBlock.Text = text;
CoreSizeText.Text = text;
LogsSizeText.Text = text;
InkSizeText.Text = text;
BackupsSizeText.Text = text;
CustomSizeText.Text = text;
PluginsSizeText.Text = text;
UpdateSizeText.Text = text;
OtherSizeText.Text = text;
}
#region
private void BtnCleanLogs_Click(object sender, RoutedEventArgs e)
{
CleanWithConfirm(LocalizationHelper.GetString("Storage_Logs_Header"), LogDirs, keepRoot: true);
}
private void BtnCleanInk_Click(object sender, RoutedEventArgs e)
{
CleanWithConfirm(LocalizationHelper.GetString("Storage_Ink_Header"), InkDirs, keepRoot: true);
}
private void BtnCleanBackups_Click(object sender, RoutedEventArgs e)
{
CleanWithConfirm(LocalizationHelper.GetString("Storage_Backups_Header"), BackupDirs, keepRoot: true);
}
private void BtnCleanUpdate_Click(object sender, RoutedEventArgs e)
{
CleanWithConfirm(LocalizationHelper.GetString("Storage_Update_Header"), UpdateDirs, keepRoot: true);
}
private async void CleanWithConfirm(string displayName, string[] subDirs, bool keepRoot)
{
var result = MessageBox.Show(
string.Format(LocalizationHelper.GetString("Storage_Confirm_Body"), displayName).Replace("\\n", "\n"),
LocalizationHelper.GetString("Storage_Confirm_Title"),
MessageBoxButton.OKCancel,
MessageBoxImage.Warning);
if (result != MessageBoxResult.OK) return;
var second = MessageBox.Show(
string.Format(LocalizationHelper.GetString("Storage_Confirm_Second_Body"), displayName),
LocalizationHelper.GetString("Storage_Confirm_Second_Title"),
MessageBoxButton.OKCancel,
MessageBoxImage.Warning);
if (second != MessageBoxResult.OK) return;
try
{
await Task.Run(() =>
{
foreach (var sub in subDirs)
{
string path = Path.Combine(App.RootPath, sub);
if (!Directory.Exists(path)) continue;
if (keepRoot)
{
foreach (var file in SafeEnumerateFiles(path, recursive: true))
TryDeleteFile(file);
foreach (var dir in SafeEnumerateDirectories(path).Reverse())
TryDeleteDirectory(dir);
}
else
{
TryDeleteDirectory(path);
}
}
});
LogHelper.WriteLogToFile($"已清理「{displayName}」分类");
}
catch (Exception ex)
{
LogHelper.WriteLogToFile($"清理「{displayName}」失败: {ex.Message}", LogHelper.LogType.Error);
MessageBox.Show(
string.Format(LocalizationHelper.GetString("Storage_CleanFailed"), ex.Message),
LocalizationHelper.GetString("Storage_Confirm_Title"),
MessageBoxButton.OK, MessageBoxImage.Error);
}
await RefreshAsync();
}
#endregion
#region
private static long GetDirectorySize(string path)
{
long total = 0;
try
{
foreach (var file in SafeEnumerateFiles(path, recursive: true))
total += SafeFileLength(file);
}
catch { }
return total;
}
private static System.Collections.Generic.IEnumerable<string> SafeEnumerateDirectories(string path)
{
try { return Directory.EnumerateDirectories(path); }
catch { return System.Linq.Enumerable.Empty<string>(); }
}
private static System.Collections.Generic.IEnumerable<string> SafeEnumerateFiles(string path, bool recursive = false)
{
try
{
return Directory.EnumerateFiles(path, "*",
recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
}
catch { return System.Linq.Enumerable.Empty<string>(); }
}
private static long SafeFileLength(string path)
{
try { return new FileInfo(path).Length; }
catch { return 0; }
}
private static void TryDeleteFile(string path)
{
try { File.Delete(path); } catch { }
}
private static void TryDeleteDirectory(string path)
{
try { Directory.Delete(path, true); } catch { }
}
private static string FormatSize(long bytes)
{
if (bytes <= 0) return "0 B";
string[] units = { "B", "KB", "MB", "GB", "TB" };
double size = bytes;
int u = 0;
while (size >= 1024 && u < units.Length - 1)
{
size /= 1024;
u++;
}
return u == 0 ? $"{(long)size} {units[u]}" : $"{size:0.##} {units[u]}";
}
#endregion
}
}