Привет, я использую открытый XML для чтения Excel в своем приложении .Net. У меня есть метод, который принимает строки и столбцы в качестве входных параметров и возвращает значение для этой конкретной ячейки. Ниже моя реализация.
public List<ServicePortDto> GetServicePorts(IFormFile formFile, Dictionary<string, int> starRowForPorts)
{
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(formFile.OpenReadStream(), false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
List<int> portRows = new();
int counter = 0;
IEnumerable<Sheet> sheets = spreadSheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>().Elements<Sheet>();
foreach (var sheet in sheets)
{
int startRowPort = starRowForPorts.Where(x=>x.Key == sheet.Name.Value).Select(x => x.Value).FirstOrDefault();
string relationshipId = sheet.Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)spreadSheetDocument.WorkbookPart.GetPartById(relationshipId);
Worksheet workSheet = worksheetPart.Worksheet;
SheetData sheetData = workSheet.GetFirstChild<SheetData>();
IEnumerable<Row> rows = sheetData.Descendants<Row>();
rows = rows.Where(x=>x.RowIndex >= startRowPort);
foreach (Row row in rows)
{
if (row.RowIndex >= startRowPort)
{
string portName = GetCell(workSheet, "B", startRowPort).InnerText;
if (portName != null)
{
counter = counter + 1;
}
else
{
break;
}
}
}
}
}
return new List<ServicePortDto>();
}
Ниже приведен метод GetCell.
private static Cell GetCell(Worksheet worksheet, string columnName, int rowIndex)
{
Row row = worksheet.GetFirstChild<SheetData>().Elements<Row>().FirstOrDefault(r => r.RowIndex == rowIndex);
if (row != null)
{
return row.Elements<Cell>().FirstOrDefault(c => string.Compare(c.CellReference.Value, columnName + rowIndex, true) == 0);
}
return null;
}
Проблема с вышеуказанным методом заключается в том, что если в ячейке есть какие-либо строковые данные, они читаются как целое число. Я не уверен, почему это происходит. Целочисленные значения читаются правильно. Проблема только со строковыми/текстовыми полями. Могу ли я узнать, чего мне здесь не хватает? Может кто-нибудь помочь мне с этим? Любая помощь будет оценена по достоинству. Спасибо
В Open XML значения ячеек хранятся как общие строки или непосредственно как встроенные строки, и вам необходимо обращаться с ними соответствующим образом.
private static string GetCellValue(Cell cell, SharedStringTablePart stringTablePart)
{
if (cell.DataType != null && cell.DataType.Value == CellValues.SharedString)
{
if (stringTablePart != null)
{
SharedStringItem sharedStringItem = stringTablePart.SharedStringTable.Elements<SharedStringItem>().ElementAt(int.Parse(cell.InnerText));
return sharedStringItem.Text?.Text;
}
}
else if (cell.CellValue != null)
{
return cell.CellValue.Text;
}
return null;
}
Привет @FortyTwo, спасибо за ответ. Если в моем Excel несколько листов, что мне следует передать вышеуказанному методу? можно ли передать рабочий лист вышеуказанному методу?
Вместо Open XML SDK используйте одну из библиотек Excel высокого уровня в NuGet, например ExcelDataReader, EPPlus, ClosedXML и многие другие (некоторые из них намного быстрее). Open XML SDK имеет очень низкий уровень, почти на уровне элементов XML.
Например, используя EPPlus, весь этот код можно заменить просто:
using var fileStream=formFile.OpenReadStream();
using ExcelPackage package = new ExcelPackage(fileStream);
ExcelWorksheet sheet = package.Workbook.Worksheets[0];
var range=sheet.Cells["b:b"];
foreach(var cell in range)
{
...
}
Вы можете использовать LINQ для подсчета непустых ячеек:
var filledCount=sheet.Cells["b:b"].Where(c=>c.Value!=null).Count();
ExcelDataReader создает оболочку IDbDataReader над листом Excel, которую можно использовать для чтения данных в DataTable, отправки в базу данных и т. д.:
using var reader = ExcelReaderFactory.CreateReader(fileStream);
do
{
while (reader.Read())
{
if (!reader.IsDbNull(1))
{
...
}
}
}
Вы можете использовать это средство чтения с SqlBulkCopy для импорта данных в SQL Server:
using var bcp = new SqlBulkCopy(connection);
bcp.DestinationTableName = "SomeTable";
...
bcp.WriteToServer(reader);
Для EPPlus требуется коммерческая лицензия, а для OpenXML SDK — лицензия MIT. Просто добавьте этот комментарий сюда, чтобы, если кто-то выберет это решение, он знал об этом;).
@FortyTwo, комментарий неточный. Это двойной, а не коммерческий подход. ClosedXML и ExcelDataReader — это MIT. Все библиотеки имеют лицензии, включая Open XML SDK. . В любом случае вы платите за Open XML SDK своим (или, скорее, компанией) потерянным временем и доходом. В NuGet много библиотек Excel, они намного меньше и быстрее, чем Open XML SDK.
Вместо использования низкоуровневого пакета Open XML SDK используйте такую библиотеку, как ExcelDataReader, EPPlus или одну из многих библиотек Excel в NuGet. Для того, что вы пытаетесь сделать, может потребоваться только одна строка. Кстати, что ты пытаешься сделать? Чтение одной ячейки во всех библиотеках тривиально и бесполезно.