feat(03-03): implement StorageCsvExportService
- Replace string.Empty stub with full BuildCsv implementation - UTF-8 BOM header row: Library,Site,Files,Total Size (MB),Version Size (MB),Last Modified - RFC 4180 CSV quoting via Csv() helper - FormatMb() converts bytes to MB with 2 decimal places - Add explicit System.IO using (required in WPF project)
This commit is contained in:
@@ -1,14 +1,56 @@
|
|||||||
using SharepointToolbox.Core.Models;
|
using SharepointToolbox.Core.Models;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace SharepointToolbox.Services.Export;
|
namespace SharepointToolbox.Services.Export;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exports a flat list of StorageNode objects to a UTF-8 BOM CSV.
|
||||||
|
/// Compatible with Microsoft Excel (BOM signals UTF-8 encoding).
|
||||||
|
/// </summary>
|
||||||
public class StorageCsvExportService
|
public class StorageCsvExportService
|
||||||
{
|
{
|
||||||
public string BuildCsv(IReadOnlyList<StorageNode> nodes) => string.Empty; // implemented in Plan 03-03
|
public string BuildCsv(IReadOnlyList<StorageNode> nodes)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
// Header
|
||||||
|
sb.AppendLine("Library,Site,Files,Total Size (MB),Version Size (MB),Last Modified");
|
||||||
|
|
||||||
|
foreach (var node in nodes)
|
||||||
|
{
|
||||||
|
sb.AppendLine(string.Join(",",
|
||||||
|
Csv(node.Name),
|
||||||
|
Csv(node.SiteTitle),
|
||||||
|
node.TotalFileCount.ToString(),
|
||||||
|
FormatMb(node.TotalSizeBytes),
|
||||||
|
FormatMb(node.VersionSizeBytes),
|
||||||
|
node.LastModified.HasValue
|
||||||
|
? Csv(node.LastModified.Value.ToString("yyyy-MM-dd"))
|
||||||
|
: string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task WriteAsync(IReadOnlyList<StorageNode> nodes, string filePath, CancellationToken ct)
|
public async Task WriteAsync(IReadOnlyList<StorageNode> nodes, string filePath, CancellationToken ct)
|
||||||
{
|
{
|
||||||
var csv = BuildCsv(nodes);
|
var csv = BuildCsv(nodes);
|
||||||
await System.IO.File.WriteAllTextAsync(filePath, csv, new System.Text.UTF8Encoding(true), ct);
|
// UTF-8 with BOM for Excel compatibility
|
||||||
|
await File.WriteAllTextAsync(filePath, csv, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true), ct);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static string FormatMb(long bytes)
|
||||||
|
=> (bytes / (1024.0 * 1024.0)).ToString("F2");
|
||||||
|
|
||||||
|
/// <summary>RFC 4180 CSV field quoting.</summary>
|
||||||
|
private static string Csv(string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value)) return string.Empty;
|
||||||
|
if (value.Contains(',') || value.Contains('"') || value.Contains('\n'))
|
||||||
|
return $"\"{value.Replace("\"", "\"\"")}\"";
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user