c# - 使用 OpenXML sdk 读取 excel 文件时遇到问题

标签 c# asp.net excel openxml openxml-sdk

我有一个从 excel 文件读取并将结果存储在 DataSet 中的函数。我有另一个写入 excel 文件的函数。当我尝试从一个常规的人工生成的 excel 文件中读取时,excel 读取函数返回一个空白 DataSet,但是当我从写入函数生成的 excel 文件中读取时,它工作得很好。该函数将无法处理常规生成的 excel 文件,即使我只是复制并粘贴函数生成的 excel 文件的内容也是如此。我终于找到了它,但我不知道从这里去哪里。我的代码有问题吗?

这里是excel生成函数:

public static Boolean writeToExcel(string fileName, DataSet data)
{
    Boolean answer = false;
    using (SpreadsheetDocument excelDoc = SpreadsheetDocument.Create(tempPath + fileName, SpreadsheetDocumentType.Workbook))
    {
        WorkbookPart workbookPart = excelDoc.AddWorkbookPart();
        workbookPart.Workbook = new Workbook();
        WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
        Sheets sheets = excelDoc.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());
        Sheet sheet = new Sheet()
        {
            Id = excelDoc.WorkbookPart.GetIdOfPart(worksheetPart),
            SheetId = 1,
            Name = "Page1"
        };

        sheets.Append(sheet);

        CreateWorkSheet(worksheetPart, data);
        answer = true;
    }

    return answer;
}

private static void CreateWorkSheet(WorksheetPart worksheetPart, DataSet data)
{
    Worksheet worksheet = new Worksheet();
    SheetData sheetData = new SheetData();
    UInt32Value currRowIndex = 1U;
    int colIndex = 0;
    Row excelRow;
    DataTable table = data.Tables[0];

    for (int rowIndex = -1; rowIndex < table.Rows.Count; rowIndex++)
    {
        excelRow = new Row();
        excelRow.RowIndex = currRowIndex++;
        for (colIndex = 0; colIndex < table.Columns.Count; colIndex++)
        {
            Cell cell = new Cell()
            {
                CellReference = Convert.ToString(Convert.ToChar(65 + colIndex)),
                DataType = CellValues.String
            };

            CellValue cellValue = new CellValue();

            if (rowIndex == -1)
            {
                cellValue.Text = table.Columns[colIndex].ColumnName.ToString();
            }
            else
            {
                cellValue.Text = (table.Rows[rowIndex].ItemArray[colIndex].ToString() != "") ? table.Rows[rowIndex].ItemArray[colIndex].ToString() : "*";
            }

            cell.Append(cellValue);
            excelRow.Append(cell);
        }

        sheetData.Append(excelRow);
    }

    SheetFormatProperties formattingProps = new SheetFormatProperties()
    {
        DefaultColumnWidth = 20D,
        DefaultRowHeight = 20D
    };

    worksheet.Append(formattingProps);
    worksheet.Append(sheetData);
    worksheetPart.Worksheet = worksheet;
}

而阅读功能如下:

public static void readInventoryExcel(string fileName, ref DataSet set)
{
    using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(fileName, false))
    {
        WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
        WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
        SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
        int count = -1;
        foreach (Row r in sheetData.Elements<Row>())
        {
            if (count >= 0)
            {
                DataRow row = set.Tables[0].NewRow();
                row["SerialNumber"]         = r.ChildElements[1].InnerXml;
                row["PartNumber"]           = r.ChildElements[2].InnerXml;
                row["EntryDate"]            = r.ChildElements[3].InnerXml;
                row["RetirementDate"]       = r.ChildElements[4].InnerXml;
                row["ReasonForReplacement"] = r.ChildElements[5].InnerXml;
                row["RetirementTech"]       = r.ChildElements[6].InnerXml;
                row["IncludeInMaintenance"] = r.ChildElements[7].InnerXml;
                row["MaintenanceTech"]      = r.ChildElements[8].InnerXml;
                row["Comment"]              = r.ChildElements[9].InnerXml;
                row["Station"]              = r.ChildElements[10].InnerXml;
                row["LocationStatus"]       = r.ChildElements[11].InnerXml;
                row["AssetName"]            = r.ChildElements[12].InnerXml;
                row["InventoryType"]        = r.ChildElements[13].InnerXml;
                row["Description"]          = r.ChildElements[14].InnerXml;
                set.Tables[0].Rows.Add(row);
            }
            count++;
        }
    }
}

最佳答案

认为这是因为您只有一张工作表而 Excel 有三张。我不确定,但我认为这些表以相反的顺序返回,因此您应该更改行:

WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();

WorksheetPart worksheetPart = workbookPart.WorksheetParts.Last();

搜索 WorksheetPart 可能更安全如果您可以通过工作表名称识别它。你需要找到 Sheet首先然后使用它的 ID 找到 SheetPart :

private WorksheetPart GetWorksheetPartBySheetName(WorkbookPart workbookPart, string sheetName)
{
    //find the sheet first.
    IEnumerable<Sheet> sheets = workbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>().Where(s => s.Name == sheetName);

    if (sheets.Count() > 0)
    {
        string relationshipId = sheets.First().Id.Value;
        WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(relationshipId);
        return worksheetPart;
    }

    return null;
}

然后您可以使用:

WorksheetPart worksheetPart = GetWorksheetPartBySheetName(workbookPart, "Sheet1");

在查看您的代码时,我注意到了一些您可能(或可能不!)感兴趣的其他事情:

在您的代码中,您只阅读了 InnerXml所以这对你来说可能无关紧要,但 Excel 存储字符串的方式与你编写它们的方式不同,因此读取 Excel 生成的文件可能不会给你你期望的值。在您的示例中,您将字符串直接写入单元格,如下所示:

XML of Cell value

但 Excel 使用 SharedStrings 概念,其中所有字符串都写入一个名为 sharedStrings.xml 的单独 XML 文件。该文件包含 Excel 文件中使用的字符串和引用, 值存储在工作表 XML 的单元格值中。

sharedString.xml 看起来像这样:

Shared Strings XML

然后 Cell 看起来像这样:

Cell value with sharedString

47<v>元素是对第 47 个共享字符串的引用。请注意,生成的 XML 中的类型(t 属性)是 str但 Excel 生成文件中的类型是 s .这表示您的是内联字符串,而他们的是共享字符串。

您可以像阅读任何其他部分一样阅读 SharedStrings:

var stringTable = workbookPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();

if (stringTable != null)
{
    sharedString = stringTable.SharedStringTable.ElementAt(int.Parse(value)).InnerText;
}

其次,如果您查看代码生成的单元格引用和 Excel 生成的单元格引用,您会发现您只输出列而不输出行(例如,您输出 A 而不是 A1 )。要解决此问题,您应该更改行:

CellReference = Convert.ToString(Convert.ToChar(65 + colIndex)),

CellReference = Convert.ToString(Convert.ToChar(65 + colIndex) + rowIndex.ToString()),

希望对您有所帮助。

关于c# - 使用 OpenXML sdk 读取 excel 文件时遇到问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25165637/

相关文章:

c# - 转换运算符仅适用于数字类型.net

c# - 使用 WCF 数据服务保存对象及其相关对象

c# - 附加到文本文件

c# - 即使存在 pdb,IIS 也不会在堆栈跟踪中提供行号

excel - 如何根据单元格值返回标题名称?编程语言

c# - Javascript 上下文菜单到 C#

c# - 在遗留 .NET 平台上使用 .NET Core 包

c# - 避免非只读静态字段 - Immutability NDepend

excel - 高级筛选条件不适用于多个条件

.net - 'Microsoft.ACE.OLEDB.12.0' 提供程序未在本地计算机上注册