[Bug] SaveAs file error (Excel cell max length 32,767 characters) #327

This commit is contained in:
Wei 2022-02-12 15:51:57 +08:00
parent 5db234d019
commit f2f5e6d497
9 changed files with 79 additions and 40 deletions

View File

@ -487,11 +487,11 @@ MiniExcel.SaveAs(path, value);
#### 11. File Export
#### 11. Byte Array File Export
Since 0.21.0, when value type is `byte[]` then system will save base64 string at cell by default, and when import system can be converted to `byte[]`. And if you don't want to use it, you can set `OpenXmlConfiguration.ConvertByteArrayToBase64String` to `false`, it can improve the system efficiency.
Since 1.22.0, when value type is `byte[]` then system will save file path at cell by default, and when import system can be converted to `byte[]`. And if you don't want to use it, you can set `OpenXmlConfiguration.ConvertByteArrayToBase64String` to `false`, it can improve the system efficiency.
![image](https://user-images.githubusercontent.com/12729184/150499973-682be39a-8a98-4681-915f-45c8e8e725fb.png)
![image](https://user-images.githubusercontent.com/12729184/153702334-c3b834f4-6ae4-4ddf-bd4e-e5005d5d8c6a.png)

View File

@ -493,11 +493,11 @@ MiniExcel.SaveAs(path, value);
#### 11. 文件导出
#### 11. Byte Array 文件导出
0.21.0 开始,当值类型为 `byte[]` 系统预设会转成 base64 字串以便导入时转回 `byte[]`,如不想转换可以将 `OpenXmlConfiguration.ConvertByteArrayToBase64String` 改为 `false`,能提升系统效率。
1.22.0 开始,当值类型为 `byte[]` 系统预设会转成保存文件路径以便导入时转回 `byte[]`,如不想转换可以将 `OpenXmlConfiguration.ConvertByteArrayToBase64String` 改为 `false`,能提升系统效率。
![image](https://user-images.githubusercontent.com/12729184/150499973-682be39a-8a98-4681-915f-45c8e8e725fb.png)
![image](https://user-images.githubusercontent.com/12729184/153702334-c3b834f4-6ae4-4ddf-bd4e-e5005d5d8c6a.png)

View File

@ -496,11 +496,11 @@ MiniExcel.SaveAs(path, value);
#### 11. 文件導出
#### 11. Byte Array 文件導出
0.21.0 開始,當值類型為 `byte[]` 系統預設會轉成 base64 字串以便導入時轉回 `byte[]`,如不想轉換可以將 `OpenXmlConfiguration.ConvertByteArrayToBase64String` 改為 `false`,能提升系統效率。
1.22.0 開始,當值類型為 `byte[]` 系統預設會轉成保存文件路徑以便導入時轉回 `byte[]`,如不想轉換可以將 `OpenXmlConfiguration.ConvertByteArrayToBase64String` 改為 `false`,能提升系統效率。
![image](https://user-images.githubusercontent.com/12729184/150499973-682be39a-8a98-4681-915f-45c8e8e725fb.png)
![image](https://user-images.githubusercontent.com/12729184/153702334-c3b834f4-6ae4-4ddf-bd4e-e5005d5d8c6a.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -0,0 +1 @@
<html>Hello MiniExcel</html>

View File

@ -0,0 +1 @@
Hello MiniExcel

View File

@ -668,10 +668,22 @@ namespace MiniExcelLibs.OpenXml
{
//TODO:optimize startswith
//if str start with "data:image/png;base64," then convert to byte[] https://github.com/shps951023/MiniExcel/issues/318
if (v != null && v.StartsWith("data:image/png;base64,"))
value = Convert.FromBase64String(v.Substring(22));
if (v != null && v.StartsWith("@@@fileid@@@,"))
{
var path = v.Substring(13);
var stream = _archive.GetEntry(path).Open();
byte[] bytes;
using (var ms = new MemoryStream())
{
stream.CopyTo(ms);
bytes = ms.ToArray();
}
value = bytes;
}
else
{
value = v;
}
}
else
{

View File

@ -14,15 +14,16 @@ using static MiniExcelLibs.Utils.ImageHelper;
namespace MiniExcelLibs.OpenXml
{
internal class ImageDto
internal class FileDto
{
public string ID { get; set; } = $"R{Guid.NewGuid():N}";
public string Extension { get; set; }
public string Path { get { return $"xl/media/image{ID}.{Extension}"; } }
public string Path2 { get { return $"/xl/media/image{ID}.{Extension}"; } }
public string Path { get { return $"xl/media/{ID}.{Extension}"; } }
public string Path2 { get { return $"/xl/media/{ID}.{Extension}"; } }
public Byte[] Byte { get; set; }
public int RowIndex { get; set; }
public int CellIndex { get; set; }
public bool IsImage { get; set; } = false;
}
internal class SheetDto
{
@ -44,7 +45,7 @@ namespace MiniExcelLibs.OpenXml
private readonly bool _printHeader;
private readonly object _value;
private readonly List<SheetDto> _sheets = new List<SheetDto>();
private readonly List<ImageDto> _images = new List<ImageDto>();
private readonly List<FileDto> _files = new List<FileDto>();
public ExcelOpenXmlSheetWriter(Stream stream, object value, string sheetName, IConfiguration configuration, bool printHeader)
{
this._stream = stream;
@ -412,24 +413,31 @@ namespace MiniExcelLibs.OpenXml
{
// TODO: Setting configuration because it might have high cost?
var format = ImageHelper.GetImageFormat(bytes);
//it can't insert to zip first to avoid cache image to memory
//because sheet xml is opening.. https://github.com/shps951023/MiniExcel/issues/304#issuecomment-1017031691
//int rowIndex, int cellIndex
var file = new FileDto()
{
Byte = bytes,
RowIndex = rowIndex,
CellIndex = cellIndex
};
if (format != ImageFormat.unknown)
{
//it can't insert to zip first to avoid cache image to memory
//because sheet xml is opening.. https://github.com/shps951023/MiniExcel/issues/304#issuecomment-1017031691
//int rowIndex, int cellIndex
_images.Add(new ImageDto()
{
Extension = format.ToString(),
Byte = bytes,
RowIndex = rowIndex,
CellIndex = cellIndex
});
file.Extension = format.ToString();
file.IsImage = true;
}
else
{
file.Extension = ".bin";
}
_files.Add(file);
//TODO:Convert to base64
var base64 = $"@@@fileid@@@,{file.Path}";
v = ExcelOpenXmlUtils.EncodeXML(base64);
s = "4";
}
//TODO:Convert to base64
var base64 = $"data:image/png;base64,{System.Convert.ToBase64String(bytes)}";
v = ExcelOpenXmlUtils.EncodeXML(base64);
s = "4";
}
else if (type == typeof(DateTime))
{
@ -570,9 +578,9 @@ namespace MiniExcelLibs.OpenXml
private void GenerateEndXml()
{
//Images
//Files
{
foreach (var item in _images)
foreach (var item in _files)
{
this.CreateZipEntry(item.Path, item.Byte);
}
@ -596,7 +604,7 @@ namespace MiniExcelLibs.OpenXml
// drawing rel
{
var drawing = new StringBuilder();
foreach (var i in _images)
foreach (var i in _files.Where(w=>w.IsImage))
{
drawing.AppendLine($@"<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"" Target=""{i.Path2}"" Id=""{i.ID}"" />");
}
@ -607,7 +615,7 @@ namespace MiniExcelLibs.OpenXml
// drawing
{
var drawing = new StringBuilder();
foreach (var i in _images)
foreach (var i in _files.Where(w => w.IsImage))
{
drawing.Append($@"<xdr:oneCellAnchor>
<xdr:from>
@ -619,7 +627,7 @@ namespace MiniExcelLibs.OpenXml
<xdr:ext cx=""609600"" cy=""190500"" />
<xdr:pic>
<xdr:nvPicPr>
<xdr:cNvPr id=""{_images.IndexOf(i) + 1}"" descr="""" name=""2a3f9147-58ea-4a79-87da-7d6114c4877b"" />
<xdr:cNvPr id=""{_files.IndexOf(i) + 1}"" descr="""" name=""2a3f9147-58ea-4a79-87da-7d6114c4877b"" />
<xdr:cNvPicPr>
<a:picLocks noChangeAspect=""1"" />
</xdr:cNvPicPr>
@ -645,7 +653,6 @@ namespace MiniExcelLibs.OpenXml
}
CreateZipEntry($"xl/drawings/drawing1.xml", "application/vnd.openxmlformats-officedocument.drawing+xml",
_defaultDrawing.Replace("{{format}}", drawing.ToString()));
}
// workbook.xml 、 workbookRelsXml
@ -674,7 +681,7 @@ namespace MiniExcelLibs.OpenXml
//[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/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""/>");
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>");

View File

@ -31,6 +31,24 @@ namespace MiniExcelLibs.Tests
this.output = output;
}
[Fact]
public void TestIssue327()
{
var path = PathHelper.GetTempFilePath();
var value = new[] {
new { id = 1, file = File.ReadAllBytes(PathHelper.GetFile("images/TestIssue327.png")) },
new { id = 2, file = File.ReadAllBytes(PathHelper.GetFile("other/TestIssue327.txt")) },
new { id = 3, file = File.ReadAllBytes(PathHelper.GetFile("other/TestIssue327.html")) },
};
MiniExcel.SaveAs(path, value);
var rows = MiniExcel.Query(path,true).ToList();
Assert.Equal(value[0].file, rows[0].file);
Assert.Equal(value[1].file, rows[1].file);
Assert.Equal(value[2].file, rows[2].file);
Assert.Equal("Hello MiniExcel", Encoding.UTF8.GetString(rows[1].file));
Assert.Equal("<html>Hello MiniExcel</html>", Encoding.UTF8.GetString(rows[2].file));
}
[Fact]
public void TestIssue316()
{
@ -343,10 +361,10 @@ namespace MiniExcelLibs.Tests
{
var config = new OpenXmlConfiguration() { ConvertByteArrayToBase64String = false };
var rows = MiniExcel.Query(path, true, configuration: config).ToList();
var expectedBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAIAAAD9b0jDAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAEXRFWHRTb2Z0d2FyZQBTbmlwYXN0ZV0Xzt0AAALNSURBVEiJ7ZVLTBNBGMdndrfdIofy0ERbCgcFeYRuCy2JGOPNRA9qeIZS6YEEogQj0YMmGOqDSATxQaLRxKtRID4SgjGelUBpaQvGZ7kpII8aWtjd2dkdDxsJoS1pIh6M/k+z8833m/3+8+0OJISArRa15cT/0D8CZTYPe32+Zy+GxjzjMzOzAACDYafdZquqOG7hzJtkwUQthRC6cavv0eN+QRTBujUQQp1OV1dbffZMq1arTRaqKIok4eZTrSNjHqIo6gIIIQBgbQwpal+Z/f7dPo2GoaiNHtJut3vjPhBe7+kdfvW61Mq1nGyaX1xYjkRzsk2Z6Rm8IOTvzWs73SLwwqjHK4jCgf3lcV6VxGgiECji7AXm0gvtHYQQnue/zy8ghCRJWlxaWuV5Qsilq9cKzLYiiz04ORVLiHP6A4NPRQlhjLWsVpZlnU63Y3umRqNhGCYjPV3HsrIsMwyDsYQQejIwGEuIA/WMT1AAaDSahnoHTdPKL1vXPKVp2umoZVkWAOj1+ZOCzs7NKYTo9XqjYRcAgKIo9ZRUu9VxltGYZTQAAL5+m0kKijEmAPCrqyJCcRuOECKI4lL4ByEEYykpaE62iQIgurLi9wchhLIsry8fYwwh9PomwuEwACDbZEoKauHMgKJSU1PbOy6Hpqdpml5fPsMwn7+EOru6IYQAghKrJSloTVUFURSX02G3lRw+WulqbA4EJ9XQh4+f2s6dr65zhkLTEEIKwtqaylhCnG/fauFO1Nfde/Bw6Hm/0WiYevc+LU2vhlK2pQwNvwQAsCwrYexyOrji4lhCnOaXZRljXONoOHTk2Ju3I/5AcC3EC0JZ+cE9Bea8IqursUkUker4BsWBqpIk6aL7Sm4htzvfvByJqJORaDS3kMsvLuns6kYIJcpNCFU17pvouXlHEET1URDEnt7bo2OezbMS/vp+R3/PdfKPQ38Ccg0E/CDcpY8AAAAASUVORK5CYII=";
var actulBase64 = (string)rows[0].Image;
Assert.Equal(expectedBase64, actulBase64);
var image = (string)rows[0].Image;
Assert.StartsWith("@@@fileid@@@,xl/media/", image);
}
}
@ -367,7 +385,7 @@ namespace MiniExcelLibs.Tests
MiniExcel.SaveAs(path, value);
{
Assert.Contains("/xl/media/image", Helpers.GetZipFileContent(path, "xl/drawings/_rels/drawing1.xml.rels"));
Assert.Contains("/xl/media/", Helpers.GetZipFileContent(path, "xl/drawings/_rels/drawing1.xml.rels"));
Assert.Contains("ext cx=\"609600\" cy=\"190500\"", Helpers.GetZipFileContent(path, "xl/drawings/drawing1.xml"));
Assert.Contains("/xl/drawings/drawing1.xml", Helpers.GetZipFileContent(path, "[Content_Types].xml"));
Assert.Contains("drawing r:id=", Helpers.GetZipFileContent(path, "xl/worksheets/sheet1.xml"));