|
1 | 1 | static class StableStreamBuilder |
2 | 2 | { |
| 3 | + static DateTime stableDate = new(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc); |
| 4 | + static DateTimeOffset stableDateOffset = new(stableDate); |
| 5 | + |
3 | 6 | public static Stream Build(XLWorkbook book) |
4 | 7 | { |
5 | 8 | ForcePropsToBeStable(book); |
6 | 9 |
|
7 | | - var stream = new MemoryStream(); |
8 | | - book.SaveAs(stream); |
| 10 | + var sourceStream = new MemoryStream(); |
| 11 | + book.SaveAs(sourceStream); |
9 | 12 |
|
10 | | - using (var archive = new ZipArchive(stream, ZipArchiveMode.Update, leaveOpen: true)) |
| 13 | + var targetStream = new MemoryStream(); |
| 14 | + using (var sourceArchive = new ZipArchive(sourceStream, ZipArchiveMode.Read, leaveOpen: false)) |
| 15 | + using (var targetArchive = new ZipArchive(targetStream, ZipArchiveMode.Create, leaveOpen: true)) |
11 | 16 | { |
12 | | - RemovePsmdcp(archive); |
13 | | - |
14 | | - PatchRels(archive); |
15 | | - |
16 | | - archive.FixWriteTime(); |
| 17 | + foreach (var sourceEntry in sourceArchive.Entries) |
| 18 | + { |
| 19 | + DuplicateEntry(sourceEntry, targetArchive); |
| 20 | + } |
17 | 21 | } |
18 | 22 |
|
19 | | - stream.Position = 0; |
20 | | - return stream; |
| 23 | + targetStream.Position = 0; |
| 24 | + return targetStream; |
21 | 25 | } |
22 | 26 |
|
23 | | - static void RemovePsmdcp(ZipArchive archive) |
| 27 | + static void DuplicateEntry(ZipArchiveEntry sourceEntry, ZipArchive targetArchive) |
24 | 28 | { |
25 | | - var entry = archive.Entries.SingleOrDefault(_ => |
26 | | - _.FullName.StartsWith("package/services/metadata/core-properties/") && |
27 | | - _.Name.EndsWith("psmdcp")); |
28 | | - entry?.Delete(); |
| 29 | + if (IsPsmdcp(sourceEntry)) |
| 30 | + { |
| 31 | + return; |
| 32 | + } |
| 33 | + |
| 34 | + using var sourceStream = sourceEntry.Open(); |
| 35 | + var targetEntry = targetArchive.CreateEntry(sourceEntry.FullName, CompressionLevel.Fastest); |
| 36 | + targetEntry.LastWriteTime = stableDateOffset; |
| 37 | + using (var targetStream = targetEntry.Open()) |
| 38 | + { |
| 39 | + if (IsRels(sourceEntry)) |
| 40 | + { |
| 41 | + var xml = XDocument.Load(sourceStream); |
| 42 | + PatchRelsXml(xml); |
| 43 | + xml.Save(targetStream); |
| 44 | + } |
| 45 | + else |
| 46 | + { |
| 47 | + sourceStream.CopyTo(targetStream); |
| 48 | + } |
| 49 | + } |
29 | 50 | } |
30 | 51 |
|
31 | | - static void PatchRels(ZipArchive archive) |
| 52 | + static void PatchRelsXml(XDocument xml) |
32 | 53 | { |
33 | | - var rels = archive.Entries.Single(_ => _.FullName == "_rels/.rels"); |
34 | | - |
35 | | - var xml = ReadXml(rels); |
36 | 54 | var relationships = xml.Descendants(XName.Get("Relationship", "http://schemas.openxmlformats.org/package/2006/relationships")).ToList(); |
37 | | - var psmdcpRelationship = relationships |
| 55 | + var psmdcp = relationships |
38 | 56 | .Where(rel => |
39 | 57 | { |
40 | 58 | var target = rel.Attribute("Target"); |
41 | 59 | return target != null && |
42 | 60 | target.Value.EndsWith(".psmdcp"); |
43 | 61 | }) |
44 | 62 | .SingleOrDefault(); |
45 | | - psmdcpRelationship?.Remove(); |
| 63 | + psmdcp?.Remove(); |
46 | 64 |
|
47 | | - var workbookRelationship = relationships |
| 65 | + var workbook = relationships |
48 | 66 | .Single(_ => _.Attribute("Target")!.Value.EndsWith("xl/workbook.xml")); |
49 | | - workbookRelationship.Attribute("Id")!.SetValue("VerifyClosedXml"); |
50 | | - |
51 | | - rels.Delete(); |
52 | | - var newRels = archive.CreateEntry("_rels/.rels"); |
53 | | - using var stream = newRels.Open(); |
54 | | - xml.Save(stream); |
| 67 | + workbook.Attribute("Id")!.SetValue("VerifyClosedXml"); |
55 | 68 | } |
56 | 69 |
|
57 | | - static XDocument ReadXml(ZipArchiveEntry entry) |
58 | | - { |
59 | | - using var stream = entry.Open(); |
60 | | - return XDocument.Load(stream); |
61 | | - } |
| 70 | + static bool IsPsmdcp(ZipArchiveEntry entry) => |
| 71 | + entry.FullName.StartsWith("package/services/metadata/core-properties/") && |
| 72 | + entry.Name.EndsWith("psmdcp"); |
62 | 73 |
|
63 | | - static DateTime stableDate = new(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc); |
| 74 | + static bool IsRels(ZipArchiveEntry _) => |
| 75 | + _.FullName == "_rels/.rels"; |
64 | 76 |
|
65 | 77 | static void ForcePropsToBeStable(XLWorkbook book) |
66 | 78 | { |
|
0 commit comments