mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-11-29 10:28:20 +08:00
CSV enumeration and code reusage (#600)
* 1900 year DateTime correction * Rewrite CSV enumeration * Maximize code reusage * Reuse OADate correction * Update to latest version of CodeQL * Update CodeQL to v3 --------- Co-authored-by: Lukasz Arciszewski <lukasz.arciszewski@accenture.com>
This commit is contained in:
parent
49bc8e99ed
commit
fdfe883904
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
[*.{cs,vb}]
|
||||
|
||||
# IDE0009: Member access should be qualified.
|
||||
dotnet_style_qualification_for_event = false
|
||||
|
||||
# IDE0009: Member access should be qualified.
|
||||
dotnet_style_qualification_for_field = false
|
||||
|
||||
# IDE0009: Member access should be qualified.
|
||||
dotnet_style_qualification_for_property = false
|
||||
|
||||
# IDE0009: Member access should be qualified.
|
||||
dotnet_style_qualification_for_method = false
|
||||
|
||||
# IDE0065: Misplaced using directive
|
||||
csharp_using_directive_placement = outside_namespace
|
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@ -53,7 +53,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@ -67,4 +67,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
@ -42,100 +42,94 @@ namespace MiniExcelLibs.Csv
|
||||
}
|
||||
|
||||
var type = _value.GetType();
|
||||
Type genericType = null;
|
||||
|
||||
if (_value is IDataReader)
|
||||
if (_value is IDataReader dataReader)
|
||||
{
|
||||
GenerateSheetByIDataReader(_value, seperator, newLine, _writer);
|
||||
GenerateSheetByIDataReader(dataReader, seperator, newLine, _writer);
|
||||
}
|
||||
else if (_value is IEnumerable)
|
||||
else if (_value is IEnumerable enumerable)
|
||||
{
|
||||
var values = _value as IEnumerable;
|
||||
List<object> keys = new List<object>();
|
||||
List<ExcelColumnInfo> props = null;
|
||||
string mode = null;
|
||||
|
||||
// check mode
|
||||
{
|
||||
foreach (var item in values) //TODO: need to optimize
|
||||
{
|
||||
if (item != null && mode == null)
|
||||
{
|
||||
if (item is IDictionary<string, object>)
|
||||
{
|
||||
var item2 = item as IDictionary<string, object>;
|
||||
mode = "IDictionary<string, object>";
|
||||
foreach (var key in item2.Keys)
|
||||
keys.Add(key);
|
||||
}
|
||||
else if (item is IDictionary)
|
||||
{
|
||||
var item2 = item as IDictionary;
|
||||
mode = "IDictionary";
|
||||
foreach (var key in item2.Keys)
|
||||
keys.Add(key);
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = "Properties";
|
||||
genericType = item.GetType();
|
||||
props = CustomPropertyHelper.GetSaveAsProperties(genericType, _configuration);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if(mode == null)
|
||||
// throw new NotImplementedException($"Type {type?.Name} & genericType {genericType?.Name} not Implemented. please issue for me.");
|
||||
|
||||
if (keys.Count == 0 && props == null)
|
||||
{
|
||||
_writer.Write(newLine);
|
||||
this._writer.Flush();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._printHeader)
|
||||
{
|
||||
if (props != null)
|
||||
{
|
||||
_writer.Write(string.Join(seperator, props.Select(s => CsvHelpers.ConvertToCsvValue(s?.ExcelColumnName, _configuration.AlwaysQuote, _configuration.Seperator))));
|
||||
_writer.Write(newLine);
|
||||
}
|
||||
else if (keys.Count > 0)
|
||||
{
|
||||
_writer.Write(string.Join(seperator, keys.Select(s => CsvHelpers.ConvertToCsvValue(s.ToString(), _configuration.AlwaysQuote, _configuration.Seperator))));
|
||||
_writer.Write(newLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Please issue for me.");
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == "IDictionary<string, object>") //Dapper Row
|
||||
GenerateSheetByDapperRow(_writer, _value as IEnumerable, keys.Cast<string>().ToList(), seperator, newLine);
|
||||
else if (mode == "IDictionary") //IDictionary
|
||||
GenerateSheetByIDictionary(_writer, _value as IEnumerable, keys, seperator, newLine);
|
||||
else if (mode == "Properties")
|
||||
GenerateSheetByProperties(_writer, _value as IEnumerable, props, seperator, newLine);
|
||||
else
|
||||
throw new NotImplementedException($"Type {type?.Name} & genericType {genericType?.Name} not Implemented. please issue for me.");
|
||||
GenerateSheetByIEnumerable(enumerable, seperator, newLine, _writer);
|
||||
}
|
||||
else if (_value is DataTable)
|
||||
else if (_value is DataTable dataTable)
|
||||
{
|
||||
GenerateSheetByDataTable(_writer, _value as DataTable, seperator, newLine);
|
||||
GenerateSheetByDataTable(_writer, dataTable, seperator, newLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Type {type?.Name} & genericType {genericType?.Name} not Implemented. please issue for me.");
|
||||
throw new NotImplementedException($"Type {type?.Name} not Implemented. please issue for me.");
|
||||
}
|
||||
|
||||
this._writer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateSheetByIEnumerable(IEnumerable values, string seperator, string newLine, StreamWriter writer)
|
||||
{
|
||||
Type genericType = null;
|
||||
List<ExcelColumnInfo> props = null;
|
||||
string mode = null;
|
||||
|
||||
var enumerator = values.GetEnumerator();
|
||||
var empty = !enumerator.MoveNext();
|
||||
if (empty)
|
||||
{
|
||||
// only when empty IEnumerable need to check this issue #133 https://github.com/shps951023/MiniExcel/issues/133
|
||||
genericType = TypeHelper.GetGenericIEnumerables(values).FirstOrDefault();
|
||||
if (genericType == null || genericType == typeof(object) // sometime generic type will be object, e.g: https://user-images.githubusercontent.com/12729184/132812859-52984314-44d1-4ee8-9487-2d1da159f1f0.png
|
||||
|| typeof(IDictionary<string, object>).IsAssignableFrom(genericType)
|
||||
|| typeof(IDictionary).IsAssignableFrom(genericType)
|
||||
|| typeof(KeyValuePair<string, object>).IsAssignableFrom(genericType))
|
||||
{
|
||||
_writer.Write(newLine);
|
||||
this._writer.Flush();
|
||||
return;
|
||||
}
|
||||
|
||||
mode = "Properties";
|
||||
props = CustomPropertyHelper.GetSaveAsProperties(genericType, _configuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstItem = enumerator.Current;
|
||||
if (firstItem is IDictionary<string, object> genericDic)
|
||||
{
|
||||
mode = "IDictionary<string, object>";
|
||||
props = CustomPropertyHelper.GetDictionaryColumnInfo(genericDic, null, _configuration);
|
||||
}
|
||||
else if (firstItem is IDictionary dic)
|
||||
{
|
||||
mode = "IDictionary";
|
||||
props = CustomPropertyHelper.GetDictionaryColumnInfo(null, dic, _configuration);
|
||||
mode = "IDictionary";
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = "Properties";
|
||||
genericType = firstItem.GetType();
|
||||
props = CustomPropertyHelper.GetSaveAsProperties(genericType, _configuration);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._printHeader)
|
||||
{
|
||||
_writer.Write(string.Join(seperator, props.Select(s => CsvHelpers.ConvertToCsvValue(s?.ExcelColumnName, _configuration.AlwaysQuote, _configuration.Seperator))));
|
||||
_writer.Write(newLine);
|
||||
}
|
||||
|
||||
if (!empty)
|
||||
{
|
||||
if (mode == "IDictionary<string, object>") //Dapper Row
|
||||
GenerateSheetByDapperRow(_writer, enumerator, props.Select(x => x.Key.ToString()).ToList(), seperator, newLine);
|
||||
else if (mode == "IDictionary") //IDictionary
|
||||
GenerateSheetByIDictionary(_writer, enumerator, props.Select(x => x.Key).ToList(), seperator, newLine);
|
||||
else if (mode == "Properties")
|
||||
GenerateSheetByProperties(_writer, enumerator, props, seperator, newLine);
|
||||
else
|
||||
throw new NotImplementedException($"Mode for genericType {genericType?.Name} not Implemented. please issue for me.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Insert()
|
||||
{
|
||||
SaveAs();
|
||||
@ -202,34 +196,37 @@ namespace MiniExcelLibs.Csv
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateSheetByProperties(StreamWriter writer, IEnumerable value, List<ExcelColumnInfo> props, string seperator, string newLine)
|
||||
private void GenerateSheetByProperties(StreamWriter writer, IEnumerator value, List<ExcelColumnInfo> props, string seperator, string newLine)
|
||||
{
|
||||
foreach (var v in value)
|
||||
do
|
||||
{
|
||||
var v = value.Current;
|
||||
var values = props.Select(s => CsvHelpers.ConvertToCsvValue(ToCsvString(s?.Property.GetValue(v), s), _configuration.AlwaysQuote, _configuration.Seperator));
|
||||
writer.Write(string.Join(seperator, values));
|
||||
writer.Write(newLine);
|
||||
}
|
||||
} while (value.MoveNext());
|
||||
}
|
||||
|
||||
private void GenerateSheetByIDictionary(StreamWriter writer, IEnumerable value, List<object> keys, string seperator, string newLine)
|
||||
private void GenerateSheetByIDictionary(StreamWriter writer, IEnumerator value, List<object> keys, string seperator, string newLine)
|
||||
{
|
||||
foreach (IDictionary v in value)
|
||||
do
|
||||
{
|
||||
var v = (IDictionary)value.Current;
|
||||
var values = keys.Select(key => CsvHelpers.ConvertToCsvValue(ToCsvString(v[key], null), _configuration.AlwaysQuote, _configuration.Seperator));
|
||||
writer.Write(string.Join(seperator, values));
|
||||
writer.Write(newLine);
|
||||
}
|
||||
} while (value.MoveNext());
|
||||
}
|
||||
|
||||
private void GenerateSheetByDapperRow(StreamWriter writer, IEnumerable value, List<string> keys, string seperator, string newLine)
|
||||
private void GenerateSheetByDapperRow(StreamWriter writer, IEnumerator value, List<string> keys, string seperator, string newLine)
|
||||
{
|
||||
foreach (IDictionary<string, object> v in value)
|
||||
do
|
||||
{
|
||||
var v = (IDictionary<string, object>)value.Current;
|
||||
var values = keys.Select(key => CsvHelpers.ConvertToCsvValue(ToCsvString(v[key], null), _configuration.AlwaysQuote, _configuration.Seperator));
|
||||
writer.Write(string.Join(seperator, values));
|
||||
writer.Write(newLine);
|
||||
}
|
||||
} while (value.MoveNext());
|
||||
}
|
||||
|
||||
public string ToCsvString(object value, ExcelColumnInfo p)
|
||||
@ -283,5 +280,4 @@ namespace MiniExcelLibs.Csv
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -21,43 +21,43 @@ namespace MiniExcelLibs.OpenXml
|
||||
switch (_value)
|
||||
{
|
||||
case IDictionary<string, object> sheets:
|
||||
{
|
||||
var sheetId = 0;
|
||||
_sheets.RemoveAt(0);//TODO:remove
|
||||
foreach (var sheet in sheets)
|
||||
{
|
||||
var sheetId = 0;
|
||||
_sheets.RemoveAt(0);//TODO:remove
|
||||
foreach (var sheet in sheets)
|
||||
{
|
||||
sheetId++;
|
||||
var sheetInfos = GetSheetInfos(sheet.Key);
|
||||
var sheetDto = sheetInfos.ToDto(sheetId);
|
||||
_sheets.Add(sheetDto); //TODO:remove
|
||||
sheetId++;
|
||||
var sheetInfos = GetSheetInfos(sheet.Key);
|
||||
var sheetDto = sheetInfos.ToDto(sheetId);
|
||||
_sheets.Add(sheetDto); //TODO:remove
|
||||
|
||||
currentSheetIndex = sheetId;
|
||||
currentSheetIndex = sheetId;
|
||||
|
||||
await CreateSheetXmlAsync(sheet.Value, sheetDto.Path, cancellationToken);
|
||||
}
|
||||
|
||||
break;
|
||||
await CreateSheetXmlAsync(sheet.Value, sheetDto.Path, cancellationToken);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DataSet sheets:
|
||||
{
|
||||
var sheetId = 0;
|
||||
_sheets.RemoveAt(0);//TODO:remove
|
||||
foreach (DataTable dt in sheets.Tables)
|
||||
{
|
||||
var sheetId = 0;
|
||||
_sheets.RemoveAt(0);//TODO:remove
|
||||
foreach (DataTable dt in sheets.Tables)
|
||||
{
|
||||
sheetId++;
|
||||
var sheetInfos = GetSheetInfos(dt.TableName);
|
||||
var sheetDto = sheetInfos.ToDto(sheetId);
|
||||
_sheets.Add(sheetDto); //TODO:remove
|
||||
sheetId++;
|
||||
var sheetInfos = GetSheetInfos(dt.TableName);
|
||||
var sheetDto = sheetInfos.ToDto(sheetId);
|
||||
_sheets.Add(sheetDto); //TODO:remove
|
||||
|
||||
currentSheetIndex = sheetId;
|
||||
currentSheetIndex = sheetId;
|
||||
|
||||
await CreateSheetXmlAsync(dt, sheetDto.Path, cancellationToken);
|
||||
}
|
||||
|
||||
break;
|
||||
await CreateSheetXmlAsync(dt, sheetDto.Path, cancellationToken);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
//Single sheet export.
|
||||
currentSheetIndex++;
|
||||
@ -72,25 +72,8 @@ namespace MiniExcelLibs.OpenXml
|
||||
|
||||
internal async Task GenerateDefaultOpenXmlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await CreateZipEntryAsync("_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml", ExcelOpenXmlSheetWriter._defaultRels, cancellationToken);
|
||||
await CreateZipEntryAsync("xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", ExcelOpenXmlSheetWriter._defaultSharedString, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task CreateZipEntryAsync(string path, string contentType, string content, CancellationToken cancellationToken)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
using (MiniExcelAsyncStreamWriter writer = new MiniExcelAsyncStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize, cancellationToken))
|
||||
await writer.WriteAsync(content);
|
||||
if (!string.IsNullOrEmpty(contentType))
|
||||
_zipDictionary.Add(path, new ZipPackageInfo(entry, contentType));
|
||||
}
|
||||
|
||||
private async Task CreateZipEntryAsync(string path, byte[] content, CancellationToken cancellationToken)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
await zipStream.WriteAsync(content, 0, content.Length, cancellationToken);
|
||||
await CreateZipEntryAsync("_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml", _defaultRels, cancellationToken);
|
||||
await CreateZipEntryAsync("xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", _defaultSharedString, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task CreateSheetXmlAsync(object value, string sheetPath, CancellationToken cancellationToken)
|
||||
@ -244,13 +227,13 @@ namespace MiniExcelLibs.OpenXml
|
||||
if (firstItem is IDictionary<string, object> genericDic)
|
||||
{
|
||||
mode = "IDictionary<string, object>";
|
||||
props = GetDictionaryColumnInfo(genericDic, null);
|
||||
props = CustomPropertyHelper.GetDictionaryColumnInfo(genericDic, null, _configuration);
|
||||
maxColumnIndex = props.Count;
|
||||
}
|
||||
else if (firstItem is IDictionary dic)
|
||||
{
|
||||
mode = "IDictionary";
|
||||
props = GetDictionaryColumnInfo(null, dic);
|
||||
props = CustomPropertyHelper.GetDictionaryColumnInfo(null, dic, _configuration);
|
||||
//maxColumnIndex = dic.Keys.Count;
|
||||
maxColumnIndex = props.Count; // why not using keys, because ignore attribute ![image](https://user-images.githubusercontent.com/12729184/163686902-286abb70-877b-4e84-bd3b-001ad339a84a.png)
|
||||
}
|
||||
@ -416,7 +399,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
|
||||
var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex);
|
||||
WriteCAsync(writer, r, columnName: p.ExcelColumnName);
|
||||
await WriteCAsync(writer, r, columnName: p.ExcelColumnName);
|
||||
xIndex++;
|
||||
}
|
||||
|
||||
@ -450,10 +433,14 @@ namespace MiniExcelLibs.OpenXml
|
||||
var v = tuple.Item3;
|
||||
|
||||
if (v != null && (v.StartsWith(" ", StringComparison.Ordinal) || v.EndsWith(" ", StringComparison.Ordinal))) /*Prefix and suffix blank space will lost after SaveAs #294*/
|
||||
{
|
||||
await writer.WriteAsync($"<x:c r=\"{columname}\" {(t == null ? "" : $"t =\"{t}\"")} s=\"{s}\" xml:space=\"preserve\"><x:v>{v}</x:v></x:c>");
|
||||
}
|
||||
else
|
||||
//t check avoid format error ![image](https://user-images.githubusercontent.com/12729184/118770190-9eee3480-b8b3-11eb-9f5a-87a439f5e320.png)
|
||||
{
|
||||
//to check avoid format error ![image](https://user-images.githubusercontent.com/12729184/118770190-9eee3480-b8b3-11eb-9f5a-87a439f5e320.png)
|
||||
await writer.WriteAsync($"<x:c r=\"{columname}\" {(t == null ? "" : $"t =\"{t}\"")} s=\"{s}\"><x:v>{v}</x:v></x:c>");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> GenerateSheetByColumnInfoAsync<T>(MiniExcelAsyncStreamWriter writer, IEnumerator value, List<ExcelColumnInfo> props, int xIndex = 1, int yIndex = 1)
|
||||
@ -474,6 +461,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
cellIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
object cellValue = null;
|
||||
if (isDic)
|
||||
{
|
||||
@ -489,11 +477,12 @@ namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
cellValue = p.Property.GetValue(v);
|
||||
}
|
||||
await WriteCellAsync(writer, yIndex, cellIndex, cellValue, p);
|
||||
|
||||
await WriteCellAsync(writer, yIndex, cellIndex, cellValue, p);
|
||||
|
||||
cellIndex++;
|
||||
}
|
||||
|
||||
await writer.WriteAsync($"</x:row>");
|
||||
yIndex++;
|
||||
} while (value.MoveNext());
|
||||
@ -503,132 +492,121 @@ namespace MiniExcelLibs.OpenXml
|
||||
|
||||
private async Task GenerateEndXmlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
//Files
|
||||
await AddFilesToZipAsync(cancellationToken);
|
||||
|
||||
await GenerateStylesXmlAsync(cancellationToken);
|
||||
|
||||
await GenerateDrawinRelXmlAsync(cancellationToken);
|
||||
|
||||
await GenerateDrawingXmlAsync(cancellationToken);
|
||||
|
||||
await GenerateWorkbookXmlAsync(cancellationToken);
|
||||
|
||||
await GenerateContentTypesXmlAsync(cancellationToken);
|
||||
}
|
||||
|
||||
private async Task AddFilesToZipAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
foreach (var item in _files)
|
||||
{
|
||||
foreach (var item in _files)
|
||||
{
|
||||
await this.CreateZipEntryAsync(item.Path, item.Byte, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
// styles.xml
|
||||
{
|
||||
var styleXml = string.Empty;
|
||||
|
||||
if (_configuration.TableStyles == TableStyles.None)
|
||||
{
|
||||
styleXml = _noneStylesXml;
|
||||
}
|
||||
else if (_configuration.TableStyles == TableStyles.Default)
|
||||
{
|
||||
styleXml = _defaultStylesXml;
|
||||
}
|
||||
|
||||
await CreateZipEntryAsync(@"xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", styleXml, cancellationToken);
|
||||
}
|
||||
|
||||
// drawing rel
|
||||
{
|
||||
for (int j = 0; j < _sheets.Count; j++)
|
||||
{
|
||||
var drawing = new StringBuilder();
|
||||
foreach (var i in _files.Where(w => w.IsImage && w.SheetId == j + 1))
|
||||
{
|
||||
drawing.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"" Target=""{i.Path2}"" Id=""{i.ID}"" />");
|
||||
}
|
||||
await CreateZipEntryAsync($"xl/drawings/_rels/drawing{j + 1}.xml.rels", "",
|
||||
_defaultDrawingXmlRels.Replace("{{format}}", drawing.ToString()), cancellationToken);
|
||||
}
|
||||
|
||||
}
|
||||
// drawing
|
||||
{
|
||||
for (int j = 0; j < _sheets.Count; j++)
|
||||
{
|
||||
var drawing = new StringBuilder();
|
||||
foreach (var i in _files.Where(w => w.IsImage && w.SheetId == j + 1))
|
||||
{
|
||||
drawing.Append($@"<xdr:oneCellAnchor>
|
||||
<xdr:from>
|
||||
<xdr:col>{i.CellIndex - 1/* why -1 : https://user-images.githubusercontent.com/12729184/150460189-f08ed939-44d4-44e1-be6e-9c533ece6be8.png*/}</xdr:col>
|
||||
<xdr:colOff>0</xdr:colOff>
|
||||
<xdr:row>{i.RowIndex - 1}</xdr:row>
|
||||
<xdr:rowOff>0</xdr:rowOff>
|
||||
</xdr:from>
|
||||
<xdr:ext cx=""609600"" cy=""190500"" />
|
||||
<xdr:pic>
|
||||
<xdr:nvPicPr>
|
||||
<xdr:cNvPr id=""{_files.IndexOf(i) + 1}"" descr="""" name=""2a3f9147-58ea-4a79-87da-7d6114c4877b"" />
|
||||
<xdr:cNvPicPr>
|
||||
<a:picLocks noChangeAspect=""1"" />
|
||||
</xdr:cNvPicPr>
|
||||
</xdr:nvPicPr>
|
||||
<xdr:blipFill>
|
||||
<a:blip r:embed=""{i.ID}"" cstate=""print"" />
|
||||
<a:stretch>
|
||||
<a:fillRect />
|
||||
</a:stretch>
|
||||
</xdr:blipFill>
|
||||
<xdr:spPr>
|
||||
<a:xfrm>
|
||||
<a:off x=""0"" y=""0"" />
|
||||
<a:ext cx=""0"" cy=""0"" />
|
||||
</a:xfrm>
|
||||
<a:prstGeom prst=""rect"">
|
||||
<a:avLst />
|
||||
</a:prstGeom>
|
||||
</xdr:spPr>
|
||||
</xdr:pic>
|
||||
<xdr:clientData />
|
||||
</xdr:oneCellAnchor>");
|
||||
}
|
||||
await CreateZipEntryAsync($"xl/drawings/drawing{j + 1}.xml", "application/vnd.openxmlformats-officedocument.drawing+xml",
|
||||
_defaultDrawing.Replace("{{format}}", drawing.ToString()), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
// workbook.xml 、 workbookRelsXml
|
||||
{
|
||||
var workbookXml = new StringBuilder();
|
||||
var workbookRelsXml = new StringBuilder();
|
||||
|
||||
var sheetId = 0;
|
||||
foreach (var s in _sheets)
|
||||
{
|
||||
sheetId++;
|
||||
if (string.IsNullOrEmpty(s.State))
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{s.Name}"" sheetId=""{sheetId}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
else
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{s.Name}"" sheetId=""{sheetId}"" state=""{s.State}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
workbookRelsXml.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"" Target=""/{s.Path}"" Id=""{s.ID}"" />");
|
||||
|
||||
//TODO: support multiple drawing
|
||||
//TODO: ../drawings/drawing1.xml or /xl/drawings/drawing1.xml
|
||||
var sheetRelsXml = $@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"" Target=""../drawings/drawing{sheetId}.xml"" Id=""drawing{sheetId}"" />";
|
||||
await CreateZipEntryAsync($"xl/worksheets/_rels/sheet{s.SheetIdx}.xml.rels", "",
|
||||
_defaultSheetRelXml.Replace("{{format}}", sheetRelsXml), cancellationToken);
|
||||
}
|
||||
await CreateZipEntryAsync(@"xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
||||
_defaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString()), cancellationToken);
|
||||
await CreateZipEntryAsync(@"xl/_rels/workbook.xml.rels", "",
|
||||
_defaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()), cancellationToken);
|
||||
}
|
||||
|
||||
//[Content_Types].xml
|
||||
{
|
||||
var sb = new StringBuilder(@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?><Types xmlns=""http://schemas.openxmlformats.org/package/2006/content-types""><Default ContentType=""application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"" Extension=""bin""/><Default ContentType=""application/xml"" Extension=""xml""/><Default ContentType=""image/jpeg"" Extension=""jpg""/><Default ContentType=""image/png"" Extension=""png""/><Default ContentType=""image/gif"" Extension=""gif""/><Default ContentType=""application/vnd.openxmlformats-package.relationships+xml"" Extension=""rels""/>");
|
||||
foreach (var p in _zipDictionary)
|
||||
sb.Append($"<Override ContentType=\"{p.Value.ContentType}\" PartName=\"/{p.Key}\" />");
|
||||
sb.Append("</Types>");
|
||||
ZipArchiveEntry entry = _archive.CreateEntry("[Content_Types].xml", CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize))
|
||||
writer.Write(sb.ToString());
|
||||
await this.CreateZipEntryAsync(item.Path, item.Byte, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// styles.xml
|
||||
/// </summary>
|
||||
private async Task GenerateStylesXmlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var styleXml = GetStylesXml();
|
||||
|
||||
await CreateZipEntryAsync(
|
||||
@"xl/styles.xml",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
|
||||
styleXml,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
private async Task GenerateDrawinRelXmlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++)
|
||||
{
|
||||
var drawing = GetDrawingRelationshipXml(sheetIndex);
|
||||
await CreateZipEntryAsync($"xl/drawings/_rels/drawing{sheetIndex + 1}.xml.rels", "",
|
||||
_defaultDrawingXmlRels.Replace("{{format}}", drawing), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GenerateDrawingXmlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++)
|
||||
{
|
||||
var drawing = GetDrawingXml(sheetIndex);
|
||||
await CreateZipEntryAsync(
|
||||
$"xl/drawings/drawing{sheetIndex + 1}.xml",
|
||||
"application/vnd.openxmlformats-officedocument.drawing+xml",
|
||||
_defaultDrawing.Replace("{{format}}", drawing),
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// workbook.xml 、 workbookRelsXml
|
||||
/// </summary>
|
||||
private async Task GenerateWorkbookXmlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
GenerateWorkBookXmls(
|
||||
out StringBuilder workbookXml,
|
||||
out StringBuilder workbookRelsXml,
|
||||
out Dictionary<int, string> sheetsRelsXml);
|
||||
|
||||
foreach (var sheetRelsXml in sheetsRelsXml)
|
||||
{
|
||||
await CreateZipEntryAsync(
|
||||
$"xl/worksheets/_rels/sheet{sheetRelsXml.Key}.xml.rels",
|
||||
null,
|
||||
_defaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value),
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
await CreateZipEntryAsync(
|
||||
@"xl/workbook.xml",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
||||
_defaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString()),
|
||||
cancellationToken);
|
||||
|
||||
await CreateZipEntryAsync(
|
||||
@"xl/_rels/workbook.xml.rels",
|
||||
null,
|
||||
_defaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()),
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// [Content_Types].xml
|
||||
/// </summary>
|
||||
private async Task GenerateContentTypesXmlAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var contentTypes = GetContentTypesXml();
|
||||
|
||||
await CreateZipEntryAsync(@"[Content_Types].xml", null, contentTypes, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task CreateZipEntryAsync(string path, string contentType, string content, CancellationToken cancellationToken)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
using (MiniExcelAsyncStreamWriter writer = new MiniExcelAsyncStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize, cancellationToken))
|
||||
await writer.WriteAsync(content);
|
||||
if (!string.IsNullOrEmpty(contentType))
|
||||
_zipDictionary.Add(path, new ZipPackageInfo(entry, contentType));
|
||||
}
|
||||
|
||||
private async Task CreateZipEntryAsync(string path, byte[] content, CancellationToken cancellationToken)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
await zipStream.WriteAsync(content, 0, content.Length, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using MiniExcelLibs.Zip;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MiniExcelLibs.OpenXml
|
||||
{
|
||||
@ -46,8 +47,8 @@ namespace MiniExcelLibs.OpenXml
|
||||
</x:cellStyleXfs>
|
||||
<x:cellXfs>
|
||||
<x:xf />
|
||||
<x:xf />
|
||||
<x:xf />
|
||||
<x:xf />
|
||||
<x:xf />
|
||||
<x:xf numFmtId=""14"" applyNumberFormat=""1"" />
|
||||
</x:cellXfs>
|
||||
</x:styleSheet>";
|
||||
@ -182,27 +183,113 @@ namespace MiniExcelLibs.OpenXml
|
||||
private static readonly string _defaultSharedString = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?><sst xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" count=\"0\" uniqueCount=\"0\"></sst>";
|
||||
private static string MinifyXml(string xml) => xml.Replace("\r", "").Replace("\n", "").Replace("\t", "");
|
||||
|
||||
internal void GenerateDefaultOpenXml()
|
||||
private string GetStylesXml()
|
||||
{
|
||||
CreateZipEntry("_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml", ExcelOpenXmlSheetWriter._defaultRels);
|
||||
CreateZipEntry("xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", ExcelOpenXmlSheetWriter._defaultSharedString);
|
||||
var styleXml = string.Empty;
|
||||
|
||||
if (_configuration.TableStyles == TableStyles.None)
|
||||
{
|
||||
styleXml = _noneStylesXml;
|
||||
}
|
||||
else if (_configuration.TableStyles == TableStyles.Default)
|
||||
{
|
||||
styleXml = _defaultStylesXml;
|
||||
}
|
||||
|
||||
return styleXml;
|
||||
}
|
||||
|
||||
private void CreateZipEntry(string path, string contentType, string content)
|
||||
private string GetDrawingRelationshipXml(int sheetIndex)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize))
|
||||
writer.Write(content);
|
||||
if (!string.IsNullOrEmpty(contentType))
|
||||
_zipDictionary.Add(path, new ZipPackageInfo(entry, contentType));
|
||||
var drawing = new StringBuilder();
|
||||
foreach (var i in _files.Where(w => w.IsImage && w.SheetId == sheetIndex + 1))
|
||||
{
|
||||
drawing.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"" Target=""{i.Path2}"" Id=""{i.ID}"" />");
|
||||
}
|
||||
|
||||
return drawing.ToString();
|
||||
}
|
||||
|
||||
private void CreateZipEntry(string path, byte[] content)
|
||||
private string GetDrawingXml(int sheetIndex)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
zipStream.Write(content, 0, content.Length);
|
||||
var drawing = new StringBuilder();
|
||||
foreach (var file in _files.Where(w => w.IsImage && w.SheetId == sheetIndex + 1))
|
||||
{
|
||||
drawing.Append($@"<xdr:oneCellAnchor>
|
||||
<xdr:from>
|
||||
<xdr:col>{file.CellIndex - 1/* why -1 : https://user-images.githubusercontent.com/12729184/150460189-f08ed939-44d4-44e1-be6e-9c533ece6be8.png*/}</xdr:col>
|
||||
<xdr:colOff>0</xdr:colOff>
|
||||
<xdr:row>{file.RowIndex - 1}</xdr:row>
|
||||
<xdr:rowOff>0</xdr:rowOff>
|
||||
</xdr:from>
|
||||
<xdr:ext cx=""609600"" cy=""190500"" />
|
||||
<xdr:pic>
|
||||
<xdr:nvPicPr>
|
||||
<xdr:cNvPr id=""{_files.IndexOf(file) + 1}"" descr="""" name=""2a3f9147-58ea-4a79-87da-7d6114c4877b"" />
|
||||
<xdr:cNvPicPr>
|
||||
<a:picLocks noChangeAspect=""1"" />
|
||||
</xdr:cNvPicPr>
|
||||
</xdr:nvPicPr>
|
||||
<xdr:blipFill>
|
||||
<a:blip r:embed=""{file.ID}"" cstate=""print"" />
|
||||
<a:stretch>
|
||||
<a:fillRect />
|
||||
</a:stretch>
|
||||
</xdr:blipFill>
|
||||
<xdr:spPr>
|
||||
<a:xfrm>
|
||||
<a:off x=""0"" y=""0"" />
|
||||
<a:ext cx=""0"" cy=""0"" />
|
||||
</a:xfrm>
|
||||
<a:prstGeom prst=""rect"">
|
||||
<a:avLst />
|
||||
</a:prstGeom>
|
||||
</xdr:spPr>
|
||||
</xdr:pic>
|
||||
<xdr:clientData />
|
||||
</xdr:oneCellAnchor>");
|
||||
}
|
||||
|
||||
return drawing.ToString();
|
||||
}
|
||||
|
||||
private void GenerateWorkBookXmls(
|
||||
out StringBuilder workbookXml,
|
||||
out StringBuilder workbookRelsXml,
|
||||
out Dictionary<int, string> sheetsRelsXml)
|
||||
{
|
||||
workbookXml = new StringBuilder();
|
||||
workbookRelsXml = new StringBuilder();
|
||||
sheetsRelsXml = new Dictionary<int, string>();
|
||||
var sheetId = 0;
|
||||
foreach (var s in _sheets)
|
||||
{
|
||||
sheetId++;
|
||||
if (string.IsNullOrEmpty(s.State))
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{ExcelOpenXmlUtils.EncodeXML(s.Name)}"" sheetId=""{sheetId}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
else
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{ExcelOpenXmlUtils.EncodeXML(s.Name)}"" sheetId=""{sheetId}"" state=""{s.State}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
|
||||
workbookRelsXml.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"" Target=""/{s.Path}"" Id=""{s.ID}"" />");
|
||||
|
||||
//TODO: support multiple drawing
|
||||
//TODO: ../drawings/drawing1.xml or /xl/drawings/drawing1.xml
|
||||
var sheetRelsXml = $@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"" Target=""../drawings/drawing{sheetId}.xml"" Id=""drawing{sheetId}"" />";
|
||||
sheetsRelsXml.Add(s.SheetIdx, sheetRelsXml);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetContentTypesXml()
|
||||
{
|
||||
var sb = new StringBuilder(@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?><Types xmlns=""http://schemas.openxmlformats.org/package/2006/content-types""><Default ContentType=""application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"" Extension=""bin""/><Default ContentType=""application/xml"" Extension=""xml""/><Default ContentType=""image/jpeg"" Extension=""jpg""/><Default ContentType=""image/png"" Extension=""png""/><Default ContentType=""image/gif"" Extension=""gif""/><Default ContentType=""application/vnd.openxmlformats-package.relationships+xml"" Extension=""rels""/>");
|
||||
foreach (var p in _zipDictionary)
|
||||
sb.Append($"<Override ContentType=\"{p.Value.ContentType}\" PartName=\"/{p.Key}\" />");
|
||||
sb.Append("</Types>");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using static MiniExcelLibs.Utils.ImageHelper;
|
||||
|
||||
namespace MiniExcelLibs.OpenXml
|
||||
@ -120,6 +121,12 @@ namespace MiniExcelLibs.OpenXml
|
||||
_archive.Dispose();
|
||||
}
|
||||
|
||||
internal void GenerateDefaultOpenXml()
|
||||
{
|
||||
CreateZipEntry("_rels/.rels", "application/vnd.openxmlformats-package.relationships+xml", _defaultRels);
|
||||
CreateZipEntry("xl/sharedStrings.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml", _defaultSharedString);
|
||||
}
|
||||
|
||||
private void GenerateSheetByEnumerable(MiniExcelStreamWriter writer, IEnumerable values)
|
||||
{
|
||||
var maxColumnIndex = 0;
|
||||
@ -168,13 +175,13 @@ namespace MiniExcelLibs.OpenXml
|
||||
if (firstItem is IDictionary<string, object> genericDic)
|
||||
{
|
||||
mode = "IDictionary<string, object>";
|
||||
props = GetDictionaryColumnInfo(genericDic, null);
|
||||
props = CustomPropertyHelper.GetDictionaryColumnInfo(genericDic, null, _configuration);
|
||||
maxColumnIndex = props.Count;
|
||||
}
|
||||
else if (firstItem is IDictionary dic)
|
||||
{
|
||||
mode = "IDictionary";
|
||||
props = GetDictionaryColumnInfo(null, dic);
|
||||
props = CustomPropertyHelper.GetDictionaryColumnInfo(null, dic, _configuration);
|
||||
//maxColumnIndex = dic.Keys.Count;
|
||||
maxColumnIndex = props.Count; // why not using keys, because ignore attribute ![image](https://user-images.githubusercontent.com/12729184/163686902-286abb70-877b-4e84-bd3b-001ad339a84a.png)
|
||||
}
|
||||
@ -303,55 +310,6 @@ namespace MiniExcelLibs.OpenXml
|
||||
_zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"));
|
||||
}
|
||||
|
||||
private List<ExcelColumnInfo> GetDictionaryColumnInfo(IDictionary<string, object> dicString, IDictionary dic)
|
||||
{
|
||||
List<ExcelColumnInfo> props;
|
||||
var _props = new List<ExcelColumnInfo>();
|
||||
if (dicString != null)
|
||||
foreach (var key in dicString.Keys)
|
||||
SetDictionaryColumnInfo(_props, key);
|
||||
else if (dic != null)
|
||||
foreach (var key in dic.Keys)
|
||||
SetDictionaryColumnInfo(_props, key);
|
||||
else
|
||||
throw new NotSupportedException("SetDictionaryColumnInfo Error");
|
||||
props = CustomPropertyHelper.SortCustomProps(_props);
|
||||
return props;
|
||||
}
|
||||
|
||||
private void SetDictionaryColumnInfo(List<ExcelColumnInfo> _props, object key)
|
||||
{
|
||||
var p = new ExcelColumnInfo();
|
||||
p.ExcelColumnName = key?.ToString();
|
||||
p.Key = key;
|
||||
// TODO:Dictionary value type is not fiexed
|
||||
//var _t =
|
||||
//var gt = Nullable.GetUnderlyingType(p.PropertyType);
|
||||
var isIgnore = false;
|
||||
if (_configuration.DynamicColumns != null && _configuration.DynamicColumns.Length > 0)
|
||||
{
|
||||
var dynamicColumn = _configuration.DynamicColumns.SingleOrDefault(_ => _.Key == key.ToString());
|
||||
if (dynamicColumn != null)
|
||||
{
|
||||
p.Nullable = true;
|
||||
//p.ExcludeNullableType = item2[key]?.GetType();
|
||||
if (dynamicColumn.Format != null)
|
||||
p.ExcelFormat = dynamicColumn.Format;
|
||||
if (dynamicColumn.Aliases != null)
|
||||
p.ExcelColumnAliases = dynamicColumn.Aliases;
|
||||
if (dynamicColumn.IndexName != null)
|
||||
p.ExcelIndexName = dynamicColumn.IndexName;
|
||||
p.ExcelColumnIndex = dynamicColumn.Index;
|
||||
if (dynamicColumn.Name != null)
|
||||
p.ExcelColumnName = dynamicColumn.Name;
|
||||
isIgnore = dynamicColumn.Ignore;
|
||||
p.ExcelColumnWidth = dynamicColumn.Width;
|
||||
}
|
||||
}
|
||||
if (!isIgnore)
|
||||
_props.Add(p);
|
||||
}
|
||||
|
||||
private void SetGenericTypePropertiesMode(Type genericType, ref string mode, out int maxColumnIndex, out List<ExcelColumnInfo> props)
|
||||
{
|
||||
mode = "Properties";
|
||||
@ -563,16 +521,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
else if (columnInfo == null || columnInfo.ExcelFormat == null)
|
||||
{
|
||||
var oaDate = ((DateTime)value).ToOADate();
|
||||
|
||||
// Excel says 1900 was a leap year :( Replicate an incorrect behavior thanks
|
||||
// to Lotus 1-2-3 decision from 1983...
|
||||
// https://github.com/ClosedXML/ClosedXML/blob/develop/ClosedXML/Extensions/DateTimeExtensions.cs#L45
|
||||
const int nonExistent1900Feb29SerialDate = 60;
|
||||
if (oaDate <= nonExistent1900Feb29SerialDate)
|
||||
{
|
||||
oaDate -= 1;
|
||||
}
|
||||
var oaDate = CorrectDateTimeValue((DateTime)value);
|
||||
|
||||
dataType = null;
|
||||
styleIndex = "3";
|
||||
@ -596,16 +545,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
else if (columnInfo == null || columnInfo.ExcelFormat == null)
|
||||
{
|
||||
var day = (DateOnly)value;
|
||||
var oaDate = day.ToDateTime(TimeOnly.MinValue).ToOADate();
|
||||
|
||||
// Excel says 1900 was a leap year :( Replicate an incorrect behavior thanks
|
||||
// to Lotus 1-2-3 decision from 1983...
|
||||
// https://github.com/ClosedXML/ClosedXML/blob/develop/ClosedXML/Extensions/DateTimeExtensions.cs#L45
|
||||
const int nonExistent1900Feb29SerialDate = 60;
|
||||
if (oaDate <= nonExistent1900Feb29SerialDate)
|
||||
{
|
||||
oaDate -= 1;
|
||||
}
|
||||
var oaDate = CorrectDateTimeValue(day.ToDateTime(TimeOnly.MinValue));
|
||||
|
||||
dataType = null;
|
||||
styleIndex = "3";
|
||||
@ -629,6 +569,22 @@ namespace MiniExcelLibs.OpenXml
|
||||
return Tuple.Create(styleIndex, dataType, cellValue);
|
||||
}
|
||||
|
||||
private static double CorrectDateTimeValue(DateTime value)
|
||||
{
|
||||
var oaDate = value.ToOADate();
|
||||
|
||||
// Excel says 1900 was a leap year :( Replicate an incorrect behavior thanks
|
||||
// to Lotus 1-2-3 decision from 1983...
|
||||
// https://github.com/ClosedXML/ClosedXML/blob/develop/ClosedXML/Extensions/DateTimeExtensions.cs#L45
|
||||
const int nonExistent1900Feb29SerialDate = 60;
|
||||
if (oaDate <= nonExistent1900Feb29SerialDate)
|
||||
{
|
||||
oaDate -= 1;
|
||||
}
|
||||
|
||||
return oaDate;
|
||||
}
|
||||
|
||||
private void GenerateSheetByDataTable(MiniExcelStreamWriter writer, DataTable value)
|
||||
{
|
||||
var xy = ExcelOpenXmlUtils.ConvertCellToXY("A1");
|
||||
@ -834,131 +790,98 @@ namespace MiniExcelLibs.OpenXml
|
||||
|
||||
private void GenerateEndXml()
|
||||
{
|
||||
//Files
|
||||
AddFilesToZip();
|
||||
|
||||
GenerateStylesXml();
|
||||
|
||||
GenerateDrawinRelXml();
|
||||
|
||||
GenerateDrawingXml();
|
||||
|
||||
GenerateWorkbookXml();
|
||||
|
||||
GenerateContentTypesXml();
|
||||
}
|
||||
|
||||
private void AddFilesToZip()
|
||||
{
|
||||
foreach (var item in _files)
|
||||
{
|
||||
foreach (var item in _files)
|
||||
{
|
||||
this.CreateZipEntry(item.Path, item.Byte);
|
||||
}
|
||||
this.CreateZipEntry(item.Path, item.Byte);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// styles.xml
|
||||
/// </summary>
|
||||
private void GenerateStylesXml()
|
||||
{
|
||||
var styleXml = GetStylesXml();
|
||||
CreateZipEntry(@"xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", styleXml);
|
||||
}
|
||||
|
||||
private void GenerateDrawinRelXml()
|
||||
{
|
||||
for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++)
|
||||
{
|
||||
var drawing = GetDrawingRelationshipXml(sheetIndex);
|
||||
CreateZipEntry(
|
||||
$"xl/drawings/_rels/drawing{sheetIndex + 1}.xml.rels",
|
||||
null,
|
||||
_defaultDrawingXmlRels.Replace("{{format}}", drawing));
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateDrawingXml()
|
||||
{
|
||||
for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++)
|
||||
{
|
||||
var drawing = GetDrawingXml(sheetIndex);
|
||||
|
||||
CreateZipEntry(
|
||||
$"xl/drawings/drawing{sheetIndex + 1}.xml",
|
||||
"application/vnd.openxmlformats-officedocument.drawing+xml",
|
||||
_defaultDrawing.Replace("{{format}}", drawing));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// workbook.xml 、 workbookRelsXml
|
||||
/// </summary>
|
||||
private void GenerateWorkbookXml()
|
||||
{
|
||||
GenerateWorkBookXmls(
|
||||
out StringBuilder workbookXml,
|
||||
out StringBuilder workbookRelsXml,
|
||||
out Dictionary<int, string> sheetsRelsXml);
|
||||
|
||||
foreach (var sheetRelsXml in sheetsRelsXml)
|
||||
{
|
||||
CreateZipEntry(
|
||||
$"xl/worksheets/_rels/sheet{sheetRelsXml.Key}.xml.rels",
|
||||
null,
|
||||
_defaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value));
|
||||
}
|
||||
|
||||
// styles.xml
|
||||
{
|
||||
var styleXml = string.Empty;
|
||||
CreateZipEntry(
|
||||
@"xl/workbook.xml",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
||||
_defaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString()));
|
||||
|
||||
if (_configuration.TableStyles == TableStyles.None)
|
||||
{
|
||||
styleXml = _noneStylesXml;
|
||||
}
|
||||
else if (_configuration.TableStyles == TableStyles.Default)
|
||||
{
|
||||
styleXml = _defaultStylesXml;
|
||||
}
|
||||
CreateZipEntry(@"xl/styles.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml", styleXml);
|
||||
}
|
||||
CreateZipEntry(
|
||||
@"xl/_rels/workbook.xml.rels",
|
||||
null,
|
||||
_defaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()));
|
||||
}
|
||||
|
||||
// drawing rel
|
||||
{
|
||||
for (int j = 0; j < _sheets.Count; j++)
|
||||
{
|
||||
var drawing = new StringBuilder();
|
||||
foreach (var i in _files.Where(w => w.IsImage && w.SheetId == j + 1))
|
||||
{
|
||||
drawing.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"" Target=""{i.Path2}"" Id=""{i.ID}"" />");
|
||||
}
|
||||
CreateZipEntry($"xl/drawings/_rels/drawing{j + 1}.xml.rels", "",
|
||||
_defaultDrawingXmlRels.Replace("{{format}}", drawing.ToString()));
|
||||
}
|
||||
/// <summary>
|
||||
/// [Content_Types].xml
|
||||
/// </summary>
|
||||
private void GenerateContentTypesXml()
|
||||
{
|
||||
var contentTypes = GetContentTypesXml();
|
||||
|
||||
}
|
||||
// drawing
|
||||
{
|
||||
for (int j = 0; j < _sheets.Count; j++)
|
||||
{
|
||||
var drawing = new StringBuilder();
|
||||
foreach (var i in _files.Where(w => w.IsImage && w.SheetId == j + 1))
|
||||
{
|
||||
drawing.Append($@"<xdr:oneCellAnchor>
|
||||
<xdr:from>
|
||||
<xdr:col>{i.CellIndex - 1/* why -1 : https://user-images.githubusercontent.com/12729184/150460189-f08ed939-44d4-44e1-be6e-9c533ece6be8.png*/}</xdr:col>
|
||||
<xdr:colOff>0</xdr:colOff>
|
||||
<xdr:row>{i.RowIndex - 1}</xdr:row>
|
||||
<xdr:rowOff>0</xdr:rowOff>
|
||||
</xdr:from>
|
||||
<xdr:ext cx=""609600"" cy=""190500"" />
|
||||
<xdr:pic>
|
||||
<xdr:nvPicPr>
|
||||
<xdr:cNvPr id=""{_files.IndexOf(i) + 1}"" descr="""" name=""2a3f9147-58ea-4a79-87da-7d6114c4877b"" />
|
||||
<xdr:cNvPicPr>
|
||||
<a:picLocks noChangeAspect=""1"" />
|
||||
</xdr:cNvPicPr>
|
||||
</xdr:nvPicPr>
|
||||
<xdr:blipFill>
|
||||
<a:blip r:embed=""{i.ID}"" cstate=""print"" />
|
||||
<a:stretch>
|
||||
<a:fillRect />
|
||||
</a:stretch>
|
||||
</xdr:blipFill>
|
||||
<xdr:spPr>
|
||||
<a:xfrm>
|
||||
<a:off x=""0"" y=""0"" />
|
||||
<a:ext cx=""0"" cy=""0"" />
|
||||
</a:xfrm>
|
||||
<a:prstGeom prst=""rect"">
|
||||
<a:avLst />
|
||||
</a:prstGeom>
|
||||
</xdr:spPr>
|
||||
</xdr:pic>
|
||||
<xdr:clientData />
|
||||
</xdr:oneCellAnchor>");
|
||||
}
|
||||
CreateZipEntry($"xl/drawings/drawing{j + 1}.xml", "application/vnd.openxmlformats-officedocument.drawing+xml",
|
||||
_defaultDrawing.Replace("{{format}}", drawing.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
// workbook.xml 、 workbookRelsXml
|
||||
{
|
||||
var workbookXml = new StringBuilder();
|
||||
var workbookRelsXml = new StringBuilder();
|
||||
|
||||
var sheetId = 0;
|
||||
foreach (var s in _sheets)
|
||||
{
|
||||
sheetId++;
|
||||
if (string.IsNullOrEmpty(s.State))
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{ExcelOpenXmlUtils.EncodeXML(s.Name)}"" sheetId=""{sheetId}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
else
|
||||
{
|
||||
workbookXml.AppendLine($@"<x:sheet name=""{ExcelOpenXmlUtils.EncodeXML(s.Name)}"" sheetId=""{sheetId}"" state=""{s.State}"" r:id=""{s.ID}"" />");
|
||||
}
|
||||
workbookRelsXml.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"" Target=""/{s.Path}"" Id=""{s.ID}"" />");
|
||||
|
||||
//TODO: support multiple drawing
|
||||
//TODO: ../drawings/drawing1.xml or /xl/drawings/drawing1.xml
|
||||
var sheetRelsXml = $@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing"" Target=""../drawings/drawing{sheetId}.xml"" Id=""drawing{sheetId}"" />";
|
||||
CreateZipEntry($"xl/worksheets/_rels/sheet{s.SheetIdx}.xml.rels", "",
|
||||
_defaultSheetRelXml.Replace("{{format}}", sheetRelsXml));
|
||||
}
|
||||
CreateZipEntry(@"xl/workbook.xml", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
|
||||
_defaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString()));
|
||||
CreateZipEntry(@"xl/_rels/workbook.xml.rels", "",
|
||||
_defaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()));
|
||||
}
|
||||
|
||||
//[Content_Types].xml
|
||||
{
|
||||
var sb = new StringBuilder(@"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?><Types xmlns=""http://schemas.openxmlformats.org/package/2006/content-types""><Default ContentType=""application/vnd.openxmlformats-officedocument.spreadsheetml.printerSettings"" Extension=""bin""/><Default ContentType=""application/xml"" Extension=""xml""/><Default ContentType=""image/jpeg"" Extension=""jpg""/><Default ContentType=""image/png"" Extension=""png""/><Default ContentType=""image/gif"" Extension=""gif""/><Default ContentType=""application/vnd.openxmlformats-package.relationships+xml"" Extension=""rels""/>");
|
||||
foreach (var p in _zipDictionary)
|
||||
sb.Append($"<Override ContentType=\"{p.Value.ContentType}\" PartName=\"/{p.Key}\" />");
|
||||
sb.Append("</Types>");
|
||||
ZipArchiveEntry entry = _archive.CreateEntry("[Content_Types].xml", CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize))
|
||||
writer.Write(sb.ToString());
|
||||
}
|
||||
CreateZipEntry(@"[Content_Types].xml", null, contentTypes);
|
||||
}
|
||||
|
||||
private string GetDimensionRef(int maxRowIndex, int maxColumnIndex)
|
||||
@ -975,9 +898,26 @@ namespace MiniExcelLibs.OpenXml
|
||||
return dimensionRef;
|
||||
}
|
||||
|
||||
private void CreateZipEntry(string path, string contentType, string content)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize))
|
||||
writer.Write(content);
|
||||
if (!string.IsNullOrEmpty(contentType))
|
||||
_zipDictionary.Add(path, new ZipPackageInfo(entry, contentType));
|
||||
}
|
||||
|
||||
private void CreateZipEntry(string path, byte[] content)
|
||||
{
|
||||
ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest);
|
||||
using (var zipStream = entry.Open())
|
||||
zipStream.Write(content, 0, content.Length);
|
||||
}
|
||||
|
||||
public void Insert()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
using MiniExcelLibs.Attributes;
|
||||
using MiniExcelLibs.OpenXml;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
@ -168,7 +169,6 @@
|
||||
return source.ToString();
|
||||
}
|
||||
|
||||
|
||||
private static IEnumerable<ExcelColumnInfo> ConvertToExcelCustomPropertyInfo(PropertyInfo[] props, Configuration configuration)
|
||||
{
|
||||
// solve : https://github.com/shps951023/MiniExcel/issues/138
|
||||
@ -214,7 +214,6 @@
|
||||
return ConvertToExcelCustomPropertyInfo(type.GetProperties(bindingFlags), configuration);
|
||||
}
|
||||
|
||||
|
||||
internal static ExcellSheetInfo GetExcellSheetInfo(Type type, Configuration configuration)
|
||||
{
|
||||
// default options
|
||||
@ -247,5 +246,65 @@
|
||||
|
||||
return sheetInfo;
|
||||
}
|
||||
|
||||
internal static List<ExcelColumnInfo> GetDictionaryColumnInfo(IDictionary<string, object> dicString, IDictionary dic, Configuration configuration)
|
||||
{
|
||||
List<ExcelColumnInfo> props;
|
||||
var _props = new List<ExcelColumnInfo>();
|
||||
if (dicString != null)
|
||||
{
|
||||
foreach (var key in dicString.Keys)
|
||||
{
|
||||
SetDictionaryColumnInfo(_props, key, configuration);
|
||||
}
|
||||
}
|
||||
else if (dic != null)
|
||||
{
|
||||
foreach (var key in dic.Keys)
|
||||
{
|
||||
SetDictionaryColumnInfo(_props, key, configuration);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("SetDictionaryColumnInfo Error");
|
||||
}
|
||||
|
||||
props = SortCustomProps(_props);
|
||||
return props;
|
||||
}
|
||||
|
||||
internal static void SetDictionaryColumnInfo(List<ExcelColumnInfo> _props, object key, Configuration configuration)
|
||||
{
|
||||
var p = new ExcelColumnInfo();
|
||||
p.ExcelColumnName = key?.ToString();
|
||||
p.Key = key;
|
||||
// TODO:Dictionary value type is not fiexed
|
||||
//var _t =
|
||||
//var gt = Nullable.GetUnderlyingType(p.PropertyType);
|
||||
var isIgnore = false;
|
||||
if (configuration.DynamicColumns != null && configuration.DynamicColumns.Length > 0)
|
||||
{
|
||||
var dynamicColumn = configuration.DynamicColumns.SingleOrDefault(_ => _.Key == key.ToString());
|
||||
if (dynamicColumn != null)
|
||||
{
|
||||
p.Nullable = true;
|
||||
//p.ExcludeNullableType = item2[key]?.GetType();
|
||||
if (dynamicColumn.Format != null)
|
||||
p.ExcelFormat = dynamicColumn.Format;
|
||||
if (dynamicColumn.Aliases != null)
|
||||
p.ExcelColumnAliases = dynamicColumn.Aliases;
|
||||
if (dynamicColumn.IndexName != null)
|
||||
p.ExcelIndexName = dynamicColumn.IndexName;
|
||||
p.ExcelColumnIndex = dynamicColumn.Index;
|
||||
if (dynamicColumn.Name != null)
|
||||
p.ExcelColumnName = dynamicColumn.Name;
|
||||
isIgnore = dynamicColumn.Ignore;
|
||||
p.ExcelColumnWidth = dynamicColumn.Width;
|
||||
}
|
||||
}
|
||||
if (!isIgnore)
|
||||
_props.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user