简体   繁体   中英

Excel file (.xlsx) created by using DocumentFormat.OpenXML needs to be repaired when opening in Excel

I have a method which create an excel file (.xlsx) from a list of strings using DocumentFormat.OpenXml. The created file needs to be repaired when I try to open it with Excel 2016. When I click "Yes" Excel shows my file correctly. Does anyone have any suggestions? Thanks in advance.

Here's my code:

private byte[] ExportDataXlsx(System.Data.Common.DbDataReader reader, string[] fields, string[] headers, string Culture) {
  System.IO.MemoryStream sw = new System.IO.MemoryStream();
  using (var workbook = Packaging.SpreadsheetDocument.Create(sw, SpreadsheetDocumentType.Workbook)) {
      
      var sheetData = CreateSheet(workbook);

      while (reader.Read()) {
          Spreadsheet.Row newRow = new Spreadsheet.Row();
          foreach (string column in fields) {
              Spreadsheet.Cell cell = new Spreadsheet.Cell();
              cell.DataType = Spreadsheet.CellValues.String;
              object value = null;
              try {
                  int index = reader.GetOrdinal(column);
                  cell.DataType = DbKymosDomainService.ToXlsType(reader.GetFieldType(index));

                  value = DbKymosDomainService.ToStringFromCulture(reader.GetValue(index), reader.GetFieldType(index), Culture);
                  if (cell.DataType == Spreadsheet.CellValues.Number){
                      value = value == null ? "" : value.ToString().Replace(",", ".");
                  }
              }
              catch { }
              cell.CellValue = new Spreadsheet.CellValue(value == null ? null : value.ToString()); //
              newRow.AppendChild(cell);
              try { var x = newRow.InnerXml; } catch { newRow.RemoveChild(cell); }
          }

          sheetData.AppendChild(newRow);
      }

      workbook.Close();
  }

  byte[] data = sw.ToArray();
  sw.Close();
  sw.Dispose();

  return data;
}

Function which create sheet:

private Spreadsheet.SheetData CreateSheet(Packaging.SpreadsheetDocument workbook)
{
    var workbookPart = workbook.AddWorkbookPart();

    workbook.WorkbookPart.Workbook = new Spreadsheet.Workbook();

    workbook.WorkbookPart.Workbook.Sheets = new Spreadsheet.Sheets();

    var sheetPart = workbook.WorkbookPart.AddNewPart<Packaging.WorksheetPart>();
    var sheetData = new Spreadsheet.SheetData();
    sheetPart.Worksheet = new Spreadsheet.Worksheet(sheetData);

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

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

    Spreadsheet.Sheet sheet = new Spreadsheet.Sheet() { Id = relationshipId, SheetId = sheetId, Name = "Export" };
    sheets.Append(sheet);

    return sheetData;
}

In my experience when a file needs to be repaired after creating it using OpenXML it means that it is missing a crucial element or the crucial element is in the wrong place. I'm having difficulty following your code so that in itself points to something being in the wrong place. Code should be sequential and self-explanatory. A few pointers however to help with getting to the root cause of your issue.

I would suggest first using ClosedXML as it takes so much strain out of the coding. https://github.com/closedxml/closedxml

Debug your code and step through each step to see what's going on.

Open the created file in OpenXML Productivity Tool https://github.com/OfficeDev/Open-XML-SDK/releases/tag/v2.5 and have a look around.

Another tool that I couldn't be without is OpenXML FileViewer: https://github.com/davecra/OpenXmlFileViewer

Lastly I always run this sub routine to validate documents I create using OpenXML:

    public static List<string> ValidateWordDocument(FileInfo filepath, ref Int32 maxerrors = 100)
{
    try
    {
        using (WordprocessingDocument wDoc = WordprocessingDocument.Open(filepath.FullName, false))
        {
            OpenXmlValidator validator = new OpenXmlValidator();
            int count = 0;
            List<string> er = new List<string>()
            {
                string.Format($"Assessment of {filepath.Name} on {DateTime.Now} yielded the following result:  {Constants.vbCrLf}")
            };

            // set at zero so that we can determine the total quantity of errors
            validator.MaxNumberOfErrors = 0;
            // String.Format("<strong> Warning : </strong>")
            foreach (ValidationErrorInfo error in validator.Validate(wDoc))
            {
                count += 1;
                if (count > maxerrors)
                    break;
                er.Add($"Error {count}{Constants.vbCrLf}" +  $"Description {error.Description}{Constants.vbCrLf}" + $"ErrorType:  {error.ErrorType}{Constants.vbCrLf}" + $"Node {error.Node}{Constants.vbCrLf}" + $"Name {error.Node.LocalName}{Constants.vbCrLf}" + $"Path {error.Path.XPath}{Constants.vbCrLf}" + $"Part: {error.Part.Uri}{Constants.vbCrLf}" + $"-------------------------------------------{Constants.vbCrLf}" + $"Outer XML: {error.Node.OuterXml}" + $"-------------------------------------------{Constants.vbCrLf}");
            }
            int validatorcount = validator.Validate(wDoc).Count;

            switch (validatorcount)
            {
                case object _ when validatorcount > maxerrors:
                    {
                        er.Add($"Returned {count - 1} as this is the Maximum Number set by the system. The actual number of errors in {filepath.Name} is {validatorcount}");
                        er.Add("A summary list of all error types encountered is given below");

                        List<string> expectedErrors = validator.Validate(wDoc).Select(_e => _e.Description).Distinct().ToList();
                        er.AddRange(expectedErrors);
                        break;
                    }

                case object _ when 1 <= validatorcount && validatorcount <= maxerrors:
                    {
                        er.Add($"Returned all {validator}  errors in {filepath.Name}");
                        break;
                    }

                case object _ when validatorcount == 0:
                    {
                        er.Add($"No Errors found in document {filepath.Name}");
                        break;
                    }
            }

            return er;

            wDoc.Close();
        }
    }
    catch (Exception ex)
    {
        Information.Err.MessageElevate();
        return null;
    }
}

It helps greatly with problem solving any potential issues.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM