1
0

fix: 修复无法计算文件大小的问题

This commit is contained in:
Ca2didi 2025-05-19 01:16:56 +08:00
parent 8a2a7d15e2
commit d9fec8e595
10 changed files with 82 additions and 44 deletions

View File

@ -21,8 +21,9 @@ public static class PathUtility
=> Path.Combine(SettingService.C.AppSetting.RepositoryPath, login, owner, repo, => Path.Combine(SettingService.C.AppSetting.RepositoryPath, login, owner, repo,
AppDefaultValues.RepoLocalStorageManagerFolder, AppDefaultValues.RepoLocalStorageDepotFolder); AppDefaultValues.RepoLocalStorageManagerFolder, AppDefaultValues.RepoLocalStorageDepotFolder);
public static string ConvertBytesToBestDisplay(ulong bytes) public static string ConvertBytesToBestDisplay(long bytes)
{ {
if (bytes < 0) return string.Empty;
string[] units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; string[] units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
int unitIndex = 0; int unitIndex = 0;
double size = bytes; double size = bytes;

View File

@ -124,14 +124,16 @@ public class LocalFileTreeAccessor
if (!WorkPath.IsPathValid(workPath) || IgnoredDirectories.Any(d => workPath.StartsWith(d))) if (!WorkPath.IsPathValid(workPath) || IgnoredDirectories.Any(d => workPath.StartsWith(d)))
continue; continue;
var modifyTime = File.GetLastWriteTimeUtc(f); var inf = new FileInfo(f);
_currentFiles.Add(workPath, new WorkspaceFile { WorkPath = workPath, ModifyTime = modifyTime }); _currentFiles.Add(workPath, new WorkspaceFile(inf.LastWriteTimeUtc, workPath, inf.Length));
} }
LastScanTimeUtc = DateTime.UtcNow; LastScanTimeUtc = DateTime.UtcNow;
// Find those are changed // Find those are changed
var changes = _currentFiles.Values.Where(v => _baseline.TryGetValue(v.WorkPath, out var fi) && fi.ModifyTime != v.ModifyTime); var changes = _currentFiles.Values.Where(v =>
_baseline.TryGetValue(v.WorkPath, out var fi) &&
(fi.ModifyTime != v.ModifyTime || fi.Size != v.Size));
var news = _currentFiles.Values.Where(v => !_baseline.ContainsKey(v.WorkPath)); var news = _currentFiles.Values.Where(v => !_baseline.ContainsKey(v.WorkPath));
var removed = _baseline.Values.Where(v => !_currentFiles.ContainsKey(v.WorkPath)); var removed = _baseline.Values.Where(v => !_currentFiles.ContainsKey(v.WorkPath));

View File

@ -955,6 +955,9 @@ namespace Flawless.Client.Remote
[JsonPropertyName("workPath")] [JsonPropertyName("workPath")]
public string WorkPath { get; set; } public string WorkPath { get; set; }
[JsonPropertyName("size")]
public long Size { get; set; }
} }

View File

@ -156,7 +156,7 @@ public class RepositoryFileTreeAccessor : IDisposable, IAsyncDisposable, IEnumer
public IEnumerator<WorkspaceFile> GetEnumerator() public IEnumerator<WorkspaceFile> GetEnumerator()
{ {
return _cached.Values return _cached.Values
.Select(cv => new WorkspaceFile(cv.FileInfo.ModifyTime, cv.FileInfo.Path)).GetEnumerator(); .Select(cv => new WorkspaceFile(cv.FileInfo.ModifyTime, cv.FileInfo.Path, (long)cv.FileInfo.Size)).GetEnumerator();
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()

View File

@ -1177,7 +1177,7 @@ public class RepositoryService : BaseService<RepositoryService>
return new( return new(
rsp.ManifestId, rsp.ManifestId,
rsp.Depot.Select(x => new DepotLabel(x.Id, x.Length)).ToArray(), rsp.Depot.Select(x => new DepotLabel(x.Id, x.Length)).ToArray(),
rsp.FilePaths.Select(x => new WorkspaceFile(x.ModifyTime.UtcDateTime, x.WorkPath)).ToArray()); rsp.FilePaths.Select(x => new WorkspaceFile(x.ModifyTime.UtcDateTime, x.WorkPath, x.Size)).ToArray());
} }
catch (Exception e) catch (Exception e)
{ {
@ -1205,8 +1205,8 @@ public class RepositoryService : BaseService<RepositoryService>
return true; return true;
} }
public async ValueTask<CommitManifest?> CommitWorkspaceAsBaselineAsync public async ValueTask<CommitManifest?> CommitWorkspaceAsync
(RepositoryModel repo, IEnumerable<ChangeInfo> changes, string message) (RepositoryModel repo, IEnumerable<ChangeInfo> changes, string message, bool forceBaseline = false)
{ {
// Check if current version is the latest // Check if current version is the latest
var api = Api.C; var api = Api.C;
@ -1233,6 +1233,12 @@ public class RepositoryService : BaseService<RepositoryService>
return null; return null;
} }
// Decide create new depot, create full depot or create addon depot.
var changesSize = localDb.LocalAccessor.Changes.Values.Sum(x => (long) x.File.Size);
var hasAddOrModify = localDb.LocalAccessor.Changes.Any(x =>
x.Value.Type == ChangeInfoType.Add || x.Value.Type == ChangeInfoType.Modify);
// Generate depot // Generate depot
var tempDepotPath = await CreateDepotIntoTempFileAsync(repo, manifestList); var tempDepotPath = await CreateDepotIntoTempFileAsync(repo, manifestList);
if (tempDepotPath == null) return null; if (tempDepotPath == null) return null;

View File

@ -32,6 +32,8 @@ public partial class LocalChangesNode : ReactiveModel
[Reactive] private DateTime? _modifiedTime; [Reactive] private DateTime? _modifiedTime;
[Reactive] private LocalChangesNode? _parent; [Reactive] private LocalChangesNode? _parent;
[Reactive] private long _size;
public bool Included public bool Included
{ {
@ -59,7 +61,8 @@ public partial class LocalChangesNode : ReactiveModel
Type = "Folder", Type = "Folder",
FullPath = folderPath, FullPath = folderPath,
Contents = new(), Contents = new(),
Parent = parent Parent = parent,
Size = -1
}; };
} }
@ -70,7 +73,8 @@ public partial class LocalChangesNode : ReactiveModel
Type = file.Type.ToString(), Type = file.Type.ToString(),
FullPath = file.File.WorkPath, FullPath = file.File.WorkPath,
ModifiedTime = file.File.ModifyTime, ModifiedTime = file.File.ModifyTime,
Parent = parent Parent = parent,
Size = (long) file.File.Size
}; };
} }
} }
@ -179,7 +183,7 @@ public partial class RepositoryViewModel : RoutableViewModelBase
new TextColumn<LocalChangesNode, string>( new TextColumn<LocalChangesNode, string>(
"Size", "Size",
n => PathUtility.ConvertBytesToBestDisplay(GetFileSize(n))), n => PathUtility.ConvertBytesToBestDisplay(n.Size)),
new TextColumn<LocalChangesNode, DateTime?>( new TextColumn<LocalChangesNode, DateTime?>(
"ModifiedTime", n => n.ModifiedTime.HasValue ? n.ModifiedTime.Value.ToLocalTime() : null), "ModifiedTime", n => n.ModifiedTime.HasValue ? n.ModifiedTime.Value.ToLocalTime() : null),
@ -221,9 +225,9 @@ public partial class RepositoryViewModel : RoutableViewModelBase
"File Type", "File Type",
n => n.Contents != null ? "Folder" : Path.GetExtension(n.FullPath)), n => n.Contents != null ? "Folder" : Path.GetExtension(n.FullPath)),
new TextColumn<LocalChangesNode, ulong>( new TextColumn<LocalChangesNode, string>(
"Size", "Size",
n => 0), n => PathUtility.ConvertBytesToBestDisplay(n.Size)),
new TextColumn<LocalChangesNode, DateTime?>( new TextColumn<LocalChangesNode, DateTime?>(
"ModifiedTime", n => n.ModifiedTime.HasValue ? n.ModifiedTime.Value.ToLocalTime() : null), "ModifiedTime", n => n.ModifiedTime.HasValue ? n.ModifiedTime.Value.ToLocalTime() : null),
@ -232,13 +236,6 @@ public partial class RepositoryViewModel : RoutableViewModelBase
_ = StartupTasksAsync(); _ = StartupTasksAsync();
} }
private ulong GetFileSize(LocalChangesNode node)
{
if (!File.Exists(node.FullPath)) return 0;
var path = Path.Combine(_wsRoot, node.FullPath);
return (ulong)new FileInfo(path).Length;
}
private async Task StartupTasksAsync() private async Task StartupTasksAsync()
@ -552,7 +549,7 @@ public partial class RepositoryViewModel : RoutableViewModelBase
} }
using var l = UIHelper.MakeLoading("Committing changes..."); using var l = UIHelper.MakeLoading("Committing changes...");
var manifest = await RepositoryService.C.CommitWorkspaceAsBaselineAsync(Repository, changes, LocalDatabase.CommitMessage!); var manifest = await RepositoryService.C.CommitWorkspaceAsync(Repository, changes, LocalDatabase.CommitMessage!);
if (manifest == null) return; if (manifest == null) return;
LocalDatabase.LocalAccessor.SetBaseline(manifest.Value.FilePaths); LocalDatabase.LocalAccessor.SetBaseline(manifest.Value.FilePaths);

View File

@ -76,6 +76,14 @@ public partial class SettingViewModel : RoutableViewModelBase
{ {
UIHelper.NotifyError(ex); UIHelper.NotifyError(ex);
} }
try
{
}
catch (Exception ex)
{
UIHelper.NotifyError(ex);
}
} }
private async Task LoadClientSettings() private async Task LoadClientSettings()

View File

@ -42,16 +42,16 @@
<u:FormItem Label="Default Storage Location"> <u:FormItem Label="Default Storage Location">
<u:PathPicker/> <u:PathPicker/>
</u:FormItem> </u:FormItem>
<u:FormGroup Header="Workspace Refresh"> <!-- <u:FormGroup Header="Workspace Refresh"> -->
<StackPanel Spacing="4"> <!-- <StackPanel Spacing="4"> -->
<CheckBox Theme="{StaticResource CardCheckBox}" Content="Open Workspace" <!-- <CheckBox Theme="{StaticResource CardCheckBox}" Content="Open Workspace" -->
IsChecked="{Binding SettingModel.RefreshWorkspaceOnOpen}"/> <!-- IsChecked="{Binding SettingModel.RefreshWorkspaceOnOpen}"/> -->
<CheckBox Theme="{StaticResource CardCheckBox}" Content="Filesystem Changed" <!-- <CheckBox Theme="{StaticResource CardCheckBox}" Content="Filesystem Changed" -->
IsChecked="{Binding SettingModel.RefreshWorkspaceOnFilesystemChanges}"/> <!-- IsChecked="{Binding SettingModel.RefreshWorkspaceOnFilesystemChanges}"/> -->
</StackPanel> <!-- </StackPanel> -->
</u:FormGroup> <!-- </u:FormGroup> -->
<u:FormGroup Header="Extern Tools"> <u:FormGroup Header="Extern Tools">
<u:PathPicker u:FormItem.Label="Open Folder"/> <!-- <u:PathPicker u:FormItem.Label="Open Folder"/> -->
<u:PathPicker u:FormItem.Label="Diff Tool"/> <u:PathPicker u:FormItem.Label="Diff Tool"/>
</u:FormGroup> </u:FormGroup>
<StackPanel Orientation="Horizontal" Spacing="4"> <StackPanel Orientation="Horizontal" Spacing="4">
@ -84,11 +84,11 @@
<Grid RowDefinitions="Auto, *" Margin="10"> <Grid RowDefinitions="Auto, *" Margin="10">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<Button Content="Refresh Users" <Button Content="Refresh"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{Binding CreateUserCommand}"/> Command="{Binding CreateUserCommand}"/>
<Button Content="Add Users" <Button Content="Add"
VerticalAlignment="Bottom" VerticalAlignment="Bottom"
HorizontalAlignment="Right" HorizontalAlignment="Right"
Command="{Binding CreateUserCommand}"/> Command="{Binding CreateUserCommand}"/>

View File

@ -6,11 +6,14 @@ public struct WorkspaceFile : IEquatable<WorkspaceFile>
public DateTime ModifyTime { get; set; } public DateTime ModifyTime { get; set; }
public string WorkPath { get; set; } public string WorkPath { get; set; }
public ulong Size { get; set; }
public WorkspaceFile(DateTime modifyTime, string workPath) public WorkspaceFile(DateTime modifyTime, string workPath, long size)
{ {
ModifyTime = modifyTime; ModifyTime = modifyTime;
WorkPath = workPath; WorkPath = workPath;
Size = (ulong) size;
} }
public bool Equals(WorkspaceFile other) public bool Equals(WorkspaceFile other)

View File

@ -410,11 +410,11 @@ public class RepositoryInnieController(
var dateTimeStr = s.Substring(0, idx); var dateTimeStr = s.Substring(0, idx);
var pathStr = s.Substring(idx + 1); var pathStr = s.Substring(idx + 1);
return new WorkspaceFile(DateTime.FromBinary(long.Parse(dateTimeStr)), pathStr); return new WorkspaceFile(DateTime.FromBinary(long.Parse(dateTimeStr)), pathStr, 0);
}).ToArray(); }).ToArray();
var sizeMap = new Dictionary<WorkspaceFile, long>();
var test = new HashSet<WorkspaceFile>(actualFs); var test = new HashSet<WorkspaceFile>(actualFs);
var createNewDepot = false;
RepositoryDepot mainDepot; RepositoryDepot mainDepot;
List<DepotLabel> depotLabels = new(); List<DepotLabel> depotLabels = new();
@ -423,7 +423,7 @@ public class RepositoryInnieController(
{ {
// Use existed depots // Use existed depots
var mainDepotId = Guid.Parse(req.MainDepotId!); var mainDepotId = Guid.Parse(req.MainDepotId!);
var preDepot = await StencilWorkspaceSnapshotViaDatabaseAsync(rp.Id, mainDepotId, test); var preDepot = await StencilWorkspaceSnapshotViaDatabaseAsync(rp.Id, mainDepotId, test, sizeMap);
if (preDepot == null) if (preDepot == null)
return BadRequest(new FailedResponse("Not a valid main depot!", "NoMainDepot")); return BadRequest(new FailedResponse("Not a valid main depot!", "NoMainDepot"));
@ -448,7 +448,7 @@ public class RepositoryInnieController(
await req.Depot.CopyToAsync(cacheStream, HttpContext.RequestAborted); await req.Depot.CopyToAsync(cacheStream, HttpContext.RequestAborted);
// Look if main depot can do this... // Look if main depot can do this...
await StencilWorkspaceSnapshotAsync(cacheStream, test); await StencilWorkspaceSnapshotAsync(cacheStream, test, sizeMap);
// Oh no, will we need to load their parents? // Oh no, will we need to load their parents?
var unresolvedCount = test.Count; var unresolvedCount = test.Count;
@ -457,7 +457,7 @@ public class RepositoryInnieController(
// Yes // Yes
foreach (var subDepot in req.RequiredDepots?.Select(Guid.Parse) ?? []) foreach (var subDepot in req.RequiredDepots?.Select(Guid.Parse) ?? [])
{ {
await StencilWorkspaceSnapshotViaDatabaseAsync(rp.Id, subDepot, test); await StencilWorkspaceSnapshotViaDatabaseAsync(rp.Id, subDepot, test, sizeMap);
var rest = test.Count; var rest = test.Count;
if (rest == 0) if (rest == 0)
@ -510,6 +510,14 @@ public class RepositoryInnieController(
depotLabels.AddRange(mainDepot.Dependencies.Select(d => new DepotLabel(d.DepotId, d.Length))); depotLabels.AddRange(mainDepot.Dependencies.Select(d => new DepotLabel(d.DepotId, d.Length)));
} }
// Update size info of FilePaths
for (var i = 0; i < actualFs.Length; i++)
{
var ws = actualFs[i];
ws.Size = (ulong)sizeMap.GetValueOrDefault(ws, 0);
actualFs[i] = ws;
}
// Create commit info // Create commit info
var commit = new RepositoryCommit var commit = new RepositoryCommit
@ -549,7 +557,8 @@ public class RepositoryInnieController(
return Ok(new CommitSuccessResponse(commit.CommittedOn, commit.Id, mainDepot.DepotId)); return Ok(new CommitSuccessResponse(commit.CommittedOn, commit.Id, mainDepot.DepotId));
} }
private static async ValueTask StencilWorkspaceSnapshotAsync(Stream depotStream, HashSet<WorkspaceFile> unresolved) private static async ValueTask StencilWorkspaceSnapshotAsync(Stream depotStream,
HashSet<WorkspaceFile> unresolved, Dictionary<WorkspaceFile, long> sizeMap)
{ {
// Get version // Get version
depotStream.Seek(0, SeekOrigin.Begin); // Fix: Get an invalid version code due to offset is bad. depotStream.Seek(0, SeekOrigin.Begin); // Fix: Get an invalid version code due to offset is bad.
@ -568,14 +577,20 @@ public class RepositoryInnieController(
{ {
ModifyTime = inf.ModifyTime, ModifyTime = inf.ModifyTime,
WorkPath = inf.Path, WorkPath = inf.Path,
Size = 0
}; };
if (!sizeMap.TryAdd(test, (long)inf.Size)) sizeMap[test] = (long)inf.Size;
unresolved.Remove(test); unresolved.Remove(test);
if (unresolved.Count == 0) break; // Early quit to avoid extra performance loss. if (unresolved.Count == 0) break; // Early quit to avoid extra performance loss.
} }
} }
private async Task<RepositoryDepot?> StencilWorkspaceSnapshotViaDatabaseAsync(Guid rpId, Guid depotId, HashSet<WorkspaceFile> unresolved) private async Task<RepositoryDepot?> StencilWorkspaceSnapshotViaDatabaseAsync(
Guid rpId, Guid depotId,
HashSet<WorkspaceFile> unresolved,
Dictionary<WorkspaceFile, long> sizeMap)
{ {
var depot = await dbContext.Repositories var depot = await dbContext.Repositories
.SelectMany(r => r.Depots) .SelectMany(r => r.Depots)
@ -583,20 +598,23 @@ public class RepositoryInnieController(
.ThenInclude(repositoryDepot => repositoryDepot.Dependencies) .ThenInclude(repositoryDepot => repositoryDepot.Dependencies)
.FirstOrDefaultAsync(r => r.DepotId == depotId); .FirstOrDefaultAsync(r => r.DepotId == depotId);
await RecurringAsync(transformer, rpId, depot, unresolved); await RecurringAsync(transformer, rpId, depot, unresolved, sizeMap);
return depot; return depot;
static async ValueTask RecurringAsync(PathTransformer transformer, Guid rpId, RepositoryDepot? depot, HashSet<WorkspaceFile> unresolved) static async ValueTask RecurringAsync(
PathTransformer transformer, Guid rpId,
RepositoryDepot? depot, HashSet<WorkspaceFile> unresolved,
Dictionary<WorkspaceFile, long> sizeMap)
{ {
if (depot == null) return; if (depot == null) return;
var path = transformer.GetDepotPath(rpId, depot.DepotId); var path = transformer.GetDepotPath(rpId, depot.DepotId);
// require do not extend lifetime scope. // require do not extend lifetime scope.
await using (var fs = new BufferedStream(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))) await using (var fs = new BufferedStream(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)))
await StencilWorkspaceSnapshotAsync(fs, unresolved); await StencilWorkspaceSnapshotAsync(fs, unresolved, sizeMap);
for (var i = 0; i < depot.Dependencies.Count && unresolved.Count > 0; i++) for (var i = 0; i < depot.Dependencies.Count && unresolved.Count > 0; i++)
await RecurringAsync(transformer, rpId, depot.Dependencies[i], unresolved); await RecurringAsync(transformer, rpId, depot.Dependencies[i], unresolved, sizeMap);
} }
} }