Skip to content

Commit dd81629

Browse files
authored
[OPT] Optimize max memory usage of SaveAsByTemplate #750 (#752)
* [OPT] Optimize max memory usage of SaveAsByTemplate #750 * Specify create and update mode
1 parent afc8447 commit dd81629

File tree

10 files changed

+564
-322
lines changed

10 files changed

+564
-322
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<Query Kind="Program">
2+
<Namespace>System.IO.Compression</Namespace>
3+
</Query>
4+
5+
void Main(){
6+
Issue750.Main();
7+
}
8+
9+
public class Issue750
10+
{
11+
public static void Main()
12+
{
13+
var templatePath = @"D:\git\MiniExcel\samples\xlsx\TestIssue20250403_SaveAsByTemplate_OPT.xlsx";
14+
var path = Path.GetTempPath() + Guid.NewGuid() + ".xlsx";
15+
Stream _outputFileStream;
16+
Console.WriteLine(path);
17+
using (var stream = File.Create(path))
18+
using (var templateStream = FileHelper.OpenSharedRead(templatePath))
19+
{
20+
_outputFileStream = stream;
21+
templateStream.CopyTo(_outputFileStream);
22+
var outputZipFile = new MiniExcelZipArchive(_outputFileStream, mode: ZipArchiveMode.Update, true, Encoding.UTF8);
23+
24+
var templateSheets = outputZipFile.Entries
25+
.Where(w =>
26+
w.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase) ||
27+
w.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase))
28+
.ToList();
29+
var templateSheet = templateSheets[0];
30+
var fullName = templateSheet.FullName;
31+
var outputSheetStream = outputZipFile.CreateEntry(fullName);
32+
using (var outputZipSheetStream = templateSheet.Open())
33+
{
34+
var doc = new XmlDocument();
35+
doc.Load(outputZipSheetStream);
36+
outputZipSheetStream.Dispose();
37+
38+
39+
40+
templateSheet.Delete(); // ZipArchiveEntry can't update directly, so need to delete then create logic
41+
42+
43+
44+
45+
XmlNamespaceManager _ns;
46+
_ns = new XmlNamespaceManager(new NameTable());
47+
_ns.AddNamespace("x", Config.SpreadsheetmlXmlns);
48+
_ns.AddNamespace("x14ac", Config.SpreadsheetmlXml_x14ac);
49+
50+
var worksheet = doc.SelectSingleNode("/x:worksheet", _ns);
51+
var sheetData = doc.SelectSingleNode("/x:worksheet/x:sheetData", _ns);
52+
var newSheetData = sheetData?.Clone(); //avoid delete lost data
53+
54+
var rows = newSheetData?.SelectNodes("x:row", _ns);
55+
56+
57+
sheetData.RemoveAll();
58+
sheetData.InnerText = "{{{{{{split}}}}}}"; //TODO: bad code smell
59+
60+
var contents = doc.InnerXml.Split(new[] { $"<sheetData>{{{{{{{{{{{{split}}}}}}}}}}}}</sheetData>" }, StringSplitOptions.None);
61+
62+
using (var writer = new StreamWriter(stream, Encoding.UTF8))
63+
{
64+
writer.Write(contents[0]);
65+
writer.Write($"<sheetData>"); // prefix problem
66+
67+
for (int i = 0; i < 10000000; i++)
68+
{
69+
writer.Write($"{Guid.NewGuid}{Guid.NewGuid}{Guid.NewGuid}{Guid.NewGuid}{Guid.NewGuid}");
70+
}
71+
writer.Write($"<sheetData>");
72+
}
73+
}
74+
}
75+
76+
}
77+
internal class Config
78+
{
79+
public const string SpreadsheetmlXmlns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
80+
public const string SpreadsheetmlXmlStrictns = "http://purl.oclc.org/ooxml/spreadsheetml/main";
81+
public const string SpreadsheetmlXmlRelationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
82+
public const string SpreadsheetmlXmlStrictRelationshipns = "http://purl.oclc.org/ooxml/officeDocument/relationships";
83+
public const string SpreadsheetmlXml_x14ac = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac";
84+
}
85+
// You can define other methods, fields, classes and namespaces here
86+
public class MiniExcelZipArchive : ZipArchive
87+
{
88+
public MiniExcelZipArchive(Stream stream, ZipArchiveMode mode, bool leaveOpen, Encoding entryNameEncoding)
89+
: base(stream, mode, leaveOpen, entryNameEncoding)
90+
{
91+
}
92+
93+
public new void Dispose()
94+
{
95+
Dispose(disposing: true);
96+
GC.SuppressFinalize(this);
97+
}
98+
}
99+
100+
internal static partial class FileHelper
101+
{
102+
public static FileStream OpenSharedRead(string path) => File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
103+
}
104+
}
8.48 KB
Binary file not shown.

src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ internal class ExcelOpenXmlSheetReader : IExcelReader
2222
internal IDictionary<int, string> _sharedStrings;
2323
private MergeCells _mergeCells;
2424
private ExcelOpenXmlStyles _style;
25-
private readonly ExcelOpenXmlZip _archive;
25+
internal readonly ExcelOpenXmlZip _archive;
2626
private readonly OpenXmlConfiguration _config;
2727

2828
private static readonly XmlReaderSettings _xmlSettings = new XmlReaderSettings
@@ -32,7 +32,7 @@ internal class ExcelOpenXmlSheetReader : IExcelReader
3232
XmlResolver = null
3333
};
3434

35-
public ExcelOpenXmlSheetReader(Stream stream, IConfiguration configuration)
35+
public ExcelOpenXmlSheetReader(Stream stream, IConfiguration configuration, bool isUpdateMode = true)
3636
{
3737
_archive = new ExcelOpenXmlZip(stream);
3838
_config = (OpenXmlConfiguration)configuration ?? OpenXmlConfiguration.DefaultConfig;

0 commit comments

Comments
 (0)