OpenXML 成功生成 Excel,但每次打开文件时都会显示错误

问题描述 投票:0回答:1

我正在创建一个简单的通用 OPENXML excel 生成器,可以将其作为 nuget 包安装,以便我们可以在整个项目中使用它生成 excel。

Excel 正确生成,当我打开它时,它显示有一些错误需要修复。一旦我修复它们,数据就会正确显示,但是当我再次保存并打开文档时,它必须再次修复它。

Excel修复记录显示此修复记录:来自/xl/tables/table1.xml部分的表(表)

Open XML SDK Tools 未指示任何验证错误

我尝试过以下方法

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Excel.Conversion.Dtos;
using Excel.Conversion.ExtensionMethods;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Excel.Conversion.OpenXML
{
    public class OpenXMLExcelConverter : IExcelConverter
    {
        private Dictionary<int, int> GetMaxCharacterWidth(SheetData sheetData)
        {
            //iterate over all cells getting a max char value for each column
            Dictionary<int, int> maxColWidth = new Dictionary<int, int>();
            var rows = sheetData.Elements<Row>();
            UInt32[] numberStyles = new UInt32[] { 5, 6, 7, 8 }; //styles that will add extra chars
            UInt32[] boldStyles = new UInt32[] { 1, 2, 3, 4, 6, 7, 8 }; //styles that will bold
            foreach (var r in rows)
            {
                var cells = r.Elements<Cell>().ToArray();

                //using cell index as my column
                for (int i = 0; i < cells.Length; i++)
                {
                    var cell = cells[i];
                    var cellValue = cell.CellValue == null ? string.Empty : cell.CellValue.InnerText;
                    var cellTextLength = cellValue.Length;

                    if (cell.StyleIndex != null && numberStyles.Contains(cell.StyleIndex))
                    {
                        int thousandCount = (int)Math.Truncate((double)cellTextLength / 4);

                        //add 3 for '.00' 
                        cellTextLength += (3 + thousandCount);
                    }

                    if (cell.StyleIndex != null && boldStyles.Contains(cell.StyleIndex))
                    {
                               cellTextLength += 1;
                    }

                    if (maxColWidth.ContainsKey(i))
                    {
                        var current = maxColWidth[i];
                        if (cellTextLength > current)
                        {
                            maxColWidth[i] = cellTextLength;
                        }
                    }
                    else
                    {
                        maxColWidth.Add(i, cellTextLength);
                    }
                }
            }

            return maxColWidth;
        }

        private Columns AutoSize(SheetData sheetData)
        {
            var maxColWidth = GetMaxCharacterWidth(sheetData);

            Columns columns = new Columns();
            //this is the width of my font - yours may be different
            double maxWidth = 7;
            foreach (var item in maxColWidth)
            {
                //width = Truncate([{Number of Characters} * {Maximum Digit Width} + {5 pixel padding}]/{Maximum Digit Width}*256)/256
                double width = Math.Truncate((item.Value * maxWidth + 5) / maxWidth * 256) / 256;

                //pixels=Truncate(((256 * {width} + Truncate(128/{Maximum Digit Width}))/256)*{Maximum Digit Width})
                double pixels = Math.Truncate(((256 * width + Math.Truncate(128 / maxWidth)) / 256) * maxWidth);

                //character width=Truncate(({pixels}-5)/{Maximum Digit Width} * 100+0.5)/100
                double charWidth = Math.Truncate((pixels - 5) / maxWidth * 100 + 0.5) / 100;

                Column col = new Column() { BestFit = true, Min = (UInt32)(item.Key + 1), Max = (UInt32)(item.Key + 1), CustomWidth = true, Width = (DoubleValue)width };
                columns.Append(col);
            }

            return columns;
        }

        public Table DefineTable(WorksheetPart worksheetPart, int rowMin, int rowMax, int colMin, int colMax)
        {
            TableDefinitionPart tableDefinitionPart = worksheetPart.AddNewPart<TableDefinitionPart>("rId" + (worksheetPart.TableDefinitionParts.Count() + 1));
            int tableNo = worksheetPart.TableDefinitionParts.Count();

            string reference = ((char)(64 + colMin)).ToString() + rowMin + ":" + ((char)(64 + colMax)).ToString() + rowMax;

            Table table = new Table() { Id = (UInt32)tableNo, Name = "Table" + tableNo, DisplayName = "Table" + tableNo, Reference = reference, TotalsRowShown = false };
            AutoFilter autoFilter = new AutoFilter() { Reference = reference };

            TableColumns tableColumns = new TableColumns() { Count = (UInt32)(colMax - colMin + 1) };
            for (int i = 0; i < (colMax - colMin + 1); i++)
            {
                tableColumns.Append(new TableColumn() { Id = (UInt32)(colMin + i), Name = "Column" + i }); //changed i+1 -> colMin + i
                                                                                                           //Add cell values (shared string)
            }

            TableStyleInfo tableStyleInfo = new TableStyleInfo() { Name = "TableStyleLight1", ShowFirstColumn = false, ShowLastColumn = false, ShowRowStripes = true, ShowColumnStripes = false };

            table.Append(autoFilter);
            table.Append(tableColumns);
            table.Append(tableStyleInfo);

            tableDefinitionPart.Table = table;

            TableParts tableParts = (TableParts)worksheetPart.Worksheet.ChildElements.Where(ce => ce is TableParts).FirstOrDefault(); // Add table parts only once
            if (tableParts is null)
            {
                tableParts = new TableParts();
                tableParts.Count = (UInt32)0;
                worksheetPart.Worksheet.Append(tableParts);
            }

            tableParts.Count += (UInt32)1;
            TablePart tablePart = new TablePart() { Id = "rId" + tableNo };

            tableParts.Append(tablePart);

            return table;
        }

        public byte[] ConvertToExcel(WorkbookDto workbook)
        {
            MemoryStream ms = new MemoryStream();
            using (var newWorkbook = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook))
            {
                var workbookPart = newWorkbook.AddWorkbookPart();

                newWorkbook.WorkbookPart.Workbook = new Workbook();

                newWorkbook.WorkbookPart.Workbook.Sheets = new Sheets();

                foreach (var table in workbook.WorkSheets)
                {
                    var sheetPart = newWorkbook.WorkbookPart.AddNewPart<WorksheetPart>();
                    var sheetData = new SheetData();
                    if (!table.AutoFitColumns)
                    {
                        sheetPart.Worksheet = new Worksheet(sheetData);
                    }

                    Sheets sheets = newWorkbook.WorkbookPart.Workbook.GetFirstChild<Sheets>();
                    string relationshipId = newWorkbook.WorkbookPart.GetIdOfPart(sheetPart);

                    uint sheetId = 1;
                    if (sheets.Elements<Sheet>().Count() > 0)
                    {
                        sheetId =
                            sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
                    }

                    Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = table.WorksheetName };
                    sheets.Append(sheet);
                    Row headerRow = new Row();

                    List<String> columns = new List<string>();
                    foreach (System.Data.DataColumn column in table.WorksheetData.Columns)
                    {
                        columns.Add(column.ColumnName.EscapeXmlCharacters());

                        Cell cell = new Cell();
                        cell.DataType = CellValues.String;
                        cell.CellValue = new CellValue(column.ColumnName.EscapeXmlCharacters());
                        headerRow.AppendChild(cell);
                    }


                    sheetData.AppendChild(headerRow);

                    foreach (System.Data.DataRow dsrow in table.WorksheetData.Rows)
                    {
                        Row newRow = new Row();
                        foreach (String col in columns)
                        {
                            Cell cell = new Cell();
                            cell.DataType = CellValues.String;
                            cell.CellValue = new CellValue(dsrow[col].ToString().EscapeXmlCharacters()); 
                            newRow.AppendChild(cell);
                        }

                        sheetData.AppendChild(newRow);
                    }

                    if (table.AutoFitColumns == true)
                    {
                        var columnData = AutoSize(sheetData);
                        sheetPart.Worksheet = new Worksheet();
                        sheetPart.Worksheet.Append(columnData);
                        sheetPart.Worksheet.Append(sheetData);
                    }
                    if (table.AddColumnTotals)
                    {
                        var tableForWorkSheet = DefineTable(sheetPart, 1, table.WorksheetData.Rows.Count, 1, table.WorksheetData.Columns.Count);
                    }
                }
            }
            return ms.ToArray();
        }
    }
}
c# excel openxml openxml-sdk openxml-table
1个回答
0
投票

可以使用了

通过更新代码来解决这个问题,如下所示:似乎表列名称需要与数据列名称匹配:

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Excel.Conversion.Dtos;
using Excel.Conversion.Enums;
using Excel.Conversion.ExtensionMethods;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Excel.Conversion.OpenXML
{
    public class OpenXMLExcelConverter : IExcelConverter
    {
        private Dictionary<int, int> GetMaxCharacterWidth(SheetData sheetData)
        {
            Dictionary<int, int> maxColWidth = new Dictionary<int, int>();
            var rows = sheetData.Elements<Row>();
            UInt32[] numberStyles = new UInt32[] { 5, 6, 7, 8 }; 
            UInt32[] boldStyles = new UInt32[] { 1, 2, 3, 4, 6, 7, 8 };
            foreach (var r in rows)
            {
                var cells = r.Elements<Cell>().ToArray();

                for (int i = 0; i < cells.Length; i++)
                {
                    var cell = cells[i];
                    var cellValue = cell.CellValue == null ? string.Empty : cell.CellValue.InnerText;
                    var cellTextLength = cellValue.Length;

                    if (cell.StyleIndex != null && numberStyles.Contains(cell.StyleIndex))
                    {
                        int thousandCount = (int)Math.Truncate((double)cellTextLength / 4);

                        cellTextLength += (3 + thousandCount);
                    }

                    if (cell.StyleIndex != null && boldStyles.Contains(cell.StyleIndex))
                    {
                        cellTextLength += 1;
                    }

                    if (maxColWidth.ContainsKey(i))
                    {
                        var current = maxColWidth[i];
                        if (cellTextLength > current)
                        {
                            maxColWidth[i] = cellTextLength;
                        }
                    }
                    else
                    {
                        maxColWidth.Add(i, cellTextLength);
                    }
                }
            }

            return maxColWidth;
        }

        private Columns AutoSize(SheetData sheetData)
        {
            var maxColWidth = GetMaxCharacterWidth(sheetData);

            Columns columns = new Columns();
            double maxWidth = 7;
            foreach (var item in maxColWidth)
            {
                double width = Math.Truncate((item.Value * maxWidth + 5) / maxWidth * 256) / 256;

                double pixels = Math.Truncate(((256 * width + Math.Truncate(128 / maxWidth)) / 256) * maxWidth);

                double charWidth = Math.Truncate((pixels - 5) / maxWidth * 100 + 0.5) / 100;

                Column col = new Column() { BestFit = true, Min = (UInt32)(item.Key + 1), Max = (UInt32)(item.Key + 1), CustomWidth = true, Width = (DoubleValue)width };
                columns.Append(col);
            }

            return columns;
        }

        public Table DefineTable(WorksheetPart worksheetPart, int rowMin, int rowMax, int colMin, int colMax, WorkSheetDto worksheetData, int tableNumber)
        {
            TableDefinitionPart tableDefinitionPart = worksheetPart.AddNewPart<TableDefinitionPart>("rId" + (worksheetPart.TableDefinitionParts.Count() + 1));
            int tableNo = worksheetPart.TableDefinitionParts.Count();

            string reference = ((char)(64 + colMin)).ToString() + rowMin + ":" + ((char)(64 + colMax)).ToString() + rowMax;

            Table table = new Table() { Id = (UInt32)tableNumber, Name = "Table" + tableNumber, DisplayName = "Table" + tableNumber, Reference = reference, TotalsRowShown = false };
            AutoFilter autoFilter = new AutoFilter() { Reference = reference };

            TableColumns tableColumns = new TableColumns() { Count = (UInt32)(colMax - colMin + 1) };
            for (int i = 0; i < (colMax - colMin + 1); i++)
            {
                tableColumns.Append(new TableColumn() { Id = (UInt32)(colMin + i), Name = worksheetData.WorksheetData.Columns[i].ColumnName.EscapeXmlCharacters() }); 
            }

            TableStyleInfo tableStyleInfo = new TableStyleInfo() { Name = worksheetData.TableStyle.ToString(), ShowFirstColumn = false, ShowLastColumn = false, ShowRowStripes = true, ShowColumnStripes = false };

            table.Append(autoFilter);
            table.Append(tableColumns);
            table.Append(tableStyleInfo);

            tableDefinitionPart.Table = table;

            TableParts tableParts = (TableParts)worksheetPart.Worksheet.ChildElements.Where(ce => ce is TableParts).FirstOrDefault(); // Add table parts only once
            if (tableParts is null)
            {
                tableParts = new TableParts();
                tableParts.Count = (UInt32)0;
                worksheetPart.Worksheet.Append(tableParts);
            }

            tableParts.Count += (UInt32)1;
            TablePart tablePart = new TablePart() { Id = "rId" + tableNo };

            tableParts.Append(tablePart);

            return table;
        }

        public static CellValues GetCellTypeEnum(string checkValue)
        {
            if (DateTime.TryParse(checkValue, out DateTime TempDate) == true)
            {
                return CellValues.String;
            }
            else if (bool.TryParse(checkValue, out bool TempBool) == true)
            {
                return CellValues.String;
            }
            else if (int.TryParse(checkValue, out int TempInt) == true)
            {
                return CellValues.Number;
            }
            else if (float.TryParse(checkValue, out float TempFloat) == true)
            {
                return CellValues.Number;
            }
            else if (decimal.TryParse(checkValue, out decimal TempDecimal) == true)
            {
                return CellValues.Number;
            }
            else
            {
                return CellValues.String;
            }
        }

        public byte[] ConvertToExcel(WorkbookDto workbook)
        {
            MemoryStream ms = new MemoryStream();
            using (var newWorkbook = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook))
            {
                var workbookPart = newWorkbook.AddWorkbookPart();

                newWorkbook.WorkbookPart.Workbook = new Workbook();

                newWorkbook.WorkbookPart.Workbook.Sheets = new Sheets();
                int tableNumber = 0;
                foreach (var table in workbook.WorkSheets)
                {
                    var sheetPart = newWorkbook.WorkbookPart.AddNewPart<WorksheetPart>();
                    var sheetData = new SheetData();
                    if (!table.AutoFitColumns)
                    {
                        sheetPart.Worksheet = new Worksheet(sheetData);
                    }

                    Sheets sheets = newWorkbook.WorkbookPart.Workbook.GetFirstChild<Sheets>();
                    string relationshipId = newWorkbook.WorkbookPart.GetIdOfPart(sheetPart);

                    uint sheetId = 1;
                    if (sheets.Elements<Sheet>().Count() > 0)
                    {
                        sheetId =
                            sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
                    }

                    Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = table.WorksheetName };
                    sheets.Append(sheet);
                    Row headerRow = new Row();

                    List<String> columns = new List<string>();
                    foreach (System.Data.DataColumn column in table.WorksheetData.Columns)
                    {
                        columns.Add(column.ColumnName.EscapeXmlCharacters());

                        Cell cell = new Cell();
                        cell.DataType = CellValues.String;
                        cell.CellValue = new CellValue(column.ColumnName.EscapeXmlCharacters());
                        headerRow.AppendChild(cell);
                    }


                    sheetData.AppendChild(headerRow);

                    foreach (System.Data.DataRow dsrow in table.WorksheetData.Rows)
                    {
                        Row newRow = new Row();
                        foreach (String col in columns)
                        {
                            Cell cell = new Cell();
                            cell.DataType = GetCellTypeEnum(dsrow[col].ToString().EscapeXmlCharacters());
                            cell.CellValue = new CellValue(dsrow[col].ToString().EscapeXmlCharacters()); 
                            newRow.AppendChild(cell);
                        }

                        sheetData.AppendChild(newRow);
                    }

                    if (table.AutoFitColumns == true)
                    {
                        var columnData = AutoSize(sheetData);
                        sheetPart.Worksheet = new Worksheet();
                        sheetPart.Worksheet.Append(columnData);
                        sheetPart.Worksheet.Append(sheetData);
                    }
                    if (table.TableStyle.HasValue)
                    {
                        tableNumber++;
                        var tableForWorkSheet = DefineTable(sheetPart, 1, table.WorksheetData.Rows.Count + 1, 1, table.WorksheetData.Columns.Count, table, tableNumber);
                    }
                }
            }
            return ms.ToArray();
        }
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.