414 lines
15 KiB
C#
414 lines
15 KiB
C#
using DocumentFormat.OpenXml.Packaging;
|
||
using DocumentFormat.OpenXml.Spreadsheet;
|
||
using Microsoft.AspNetCore.Http;
|
||
using System.Data;
|
||
using System.Text.RegularExpressions;
|
||
|
||
namespace Kit.Helpers.OpenXml.SLExcel
|
||
{
|
||
public interface IExcelReader
|
||
{
|
||
IEnumerable<SLExcelData> ReadAllExcel(IFormFile file);
|
||
SLExcelData ReadExcel(IFormFile file);
|
||
SLExcelData ReadExcel(Stream file, string sheetName, int headerIndex = 0, int? dataIdx = null);
|
||
SLExcelData ReadExcel(Stream file, string sheetName, string header, int? dataIdx = null);
|
||
bool IsExcel(Stream file);
|
||
}
|
||
|
||
public class SLExcelReader : IExcelReader
|
||
{
|
||
private string GetColumnName(string cellReference)
|
||
{
|
||
var regex = new Regex("[A-Za-z]+");
|
||
var match = regex.Match(cellReference);
|
||
|
||
return match.Value;
|
||
}
|
||
|
||
private int ConvertColumnNameToNumber(string columnName)
|
||
{
|
||
var alpha = new Regex("^[A-Z]+$");
|
||
if (!alpha.IsMatch(columnName)) throw new ArgumentException();
|
||
|
||
char[] colLetters = columnName.ToCharArray();
|
||
Array.Reverse(colLetters);
|
||
|
||
var convertedValue = 0;
|
||
for (int i = 0; i < colLetters.Length; i++)
|
||
{
|
||
char letter = colLetters[i];
|
||
int current = i == 0 ? letter - 65 : letter - 64; // ASCII 'A' = 65
|
||
convertedValue += current * (int)Math.Pow(26, i);
|
||
}
|
||
|
||
return convertedValue;
|
||
}
|
||
|
||
private IEnumerator<Cell> GetExcelCellEnumerator(Row row)
|
||
{
|
||
int currentCount = 0;
|
||
foreach (Cell cell in row.Descendants<Cell>())
|
||
{
|
||
string columnName = GetColumnName(cell.CellReference);
|
||
|
||
int currentColumnIndex = ConvertColumnNameToNumber(columnName);
|
||
|
||
for (; currentCount < currentColumnIndex; currentCount++)
|
||
{
|
||
var emptycell = new Cell() { DataType = null, CellValue = new CellValue(string.Empty) };
|
||
yield return emptycell;
|
||
}
|
||
|
||
yield return cell;
|
||
currentCount++;
|
||
}
|
||
}
|
||
|
||
private string ReadExcelCell(Cell cell, SharedStringItem[] sharedStrings)
|
||
{
|
||
if (cell == null)
|
||
{
|
||
return string.Empty;
|
||
}
|
||
string text;
|
||
var cellValue = cell.CellValue;
|
||
if (cell.DataType != null && cell.DataType == CellValues.SharedString)
|
||
{
|
||
int index = Convert.ToInt32(cell.CellValue.Text);
|
||
SharedStringItem sharedString = sharedStrings.ElementAtOrDefault(index);
|
||
text = sharedString.InnerText;
|
||
}
|
||
else
|
||
{
|
||
text = (cellValue == null) ? cell.InnerText : cellValue.Text;
|
||
}
|
||
|
||
return (text ?? string.Empty).Trim();
|
||
}
|
||
|
||
private SLExcelData ReadRowsAndSharedStrings(Stream file, string sheetName, out IList<Row> rows, out SharedStringItem[] sharedStrings)
|
||
{
|
||
var data = new SLExcelData();
|
||
// Open the excel document
|
||
rows = new List<Row>();
|
||
sharedStrings = new List<SharedStringItem>().ToArray();
|
||
try
|
||
{
|
||
var document = SpreadsheetDocument.Open(file, false);
|
||
WorkbookPart workbookPart = document.WorkbookPart;
|
||
|
||
var sheets = workbookPart.Workbook.Descendants<Sheet>();
|
||
var sheet = sheets.FirstOrDefault(x => x.Name == sheetName);
|
||
if (sheet == null)
|
||
{
|
||
data.Status.Message = $"В файле не найден лист \"{sheetName}\"";
|
||
return data;
|
||
}
|
||
|
||
data.SheetName = sheet.Name;
|
||
|
||
var workSheet = ((WorksheetPart)workbookPart.GetPartById(sheet.Id)).Worksheet;
|
||
var columns = workSheet.Descendants<Columns>().FirstOrDefault();
|
||
data.ColumnConfigurations = columns;
|
||
|
||
var sheetData = workSheet.Elements<SheetData>().First();
|
||
|
||
rows = sheetData.Elements<Row>().ToList();
|
||
|
||
if (workbookPart.SharedStringTablePart?.SharedStringTable != null)
|
||
{
|
||
sharedStrings = workbookPart.SharedStringTablePart.SharedStringTable.Descendants<SharedStringItem>().ToArray();
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
data.Status.Message = "Excel файл не прочитан";
|
||
return data;
|
||
}
|
||
return data;
|
||
}
|
||
|
||
private void FillSLExcelData(SLExcelData data, IList<Row> rows, SharedStringItem[] sharedStrings, int headerIndex, int? dataIdx)
|
||
{
|
||
// Read the header
|
||
if (rows.Count > headerIndex)
|
||
{
|
||
var row = rows[headerIndex];
|
||
var cellEnumerator = GetExcelCellEnumerator(row);
|
||
while (cellEnumerator.MoveNext())
|
||
{
|
||
var cell = cellEnumerator.Current;
|
||
var text = ReadExcelCell(cell, sharedStrings).Trim();
|
||
data.Headers.Add(text);
|
||
}
|
||
}
|
||
int dataIndex = dataIdx.HasValue ? dataIdx.Value : headerIndex + 1;
|
||
// Read the sheet data
|
||
if (rows.Count > dataIndex)
|
||
{
|
||
for (var i = dataIndex; i < rows.Count; i++)
|
||
{
|
||
var dataRow = new List<string>();
|
||
data.DataRows.Add(dataRow);
|
||
var row = rows[i];
|
||
var cellEnumerator = GetExcelCellEnumerator(row);
|
||
while (cellEnumerator.MoveNext())
|
||
{
|
||
var cell = cellEnumerator.Current;
|
||
var text = ReadExcelCell(cell, sharedStrings).Trim();
|
||
dataRow.Add(text);
|
||
}
|
||
if (data.ColumnConfigurations != null && dataRow.Count() < data.ColumnConfigurations.Count())
|
||
{
|
||
int currentCount = dataRow.Count();
|
||
for (; currentCount < data.ColumnConfigurations.Count(); currentCount++)
|
||
{
|
||
dataRow.Add(string.Empty);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public IEnumerable<SLExcelData> ReadAllExcel(IFormFile file)
|
||
{
|
||
var datas = new List<SLExcelData>();
|
||
|
||
// Check if the file is excel
|
||
if (file.Length <= 0)
|
||
{
|
||
datas.First().Status.Message = "You uploaded an empty file";
|
||
return datas;
|
||
}
|
||
|
||
if (file.ContentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||
{
|
||
datas.First().Status.Message = "Please upload a valid excel file of version 2007 and above";
|
||
return datas;
|
||
}
|
||
|
||
// Open the excel document
|
||
List<Row> rows;
|
||
SharedStringItem[] sharedStrings = new List<SharedStringItem>().ToArray();
|
||
try
|
||
{
|
||
SpreadsheetDocument document;
|
||
using (Stream stream = file.OpenReadStream())
|
||
{
|
||
document = SpreadsheetDocument.Open(stream, false);
|
||
}
|
||
WorkbookPart workbookPart = document.WorkbookPart;
|
||
|
||
var sheets = workbookPart.Workbook.Descendants<Sheet>();
|
||
|
||
foreach (var sheet in sheets)
|
||
{
|
||
var data = new SLExcelData();
|
||
data.SheetName = sheet.Name;
|
||
|
||
var workSheet = ((WorksheetPart)workbookPart.GetPartById(sheet.Id)).Worksheet;
|
||
var columns = workSheet.Descendants<Columns>().FirstOrDefault();
|
||
data.ColumnConfigurations = columns;
|
||
|
||
var sheetData = workSheet.Elements<SheetData>().First();
|
||
|
||
rows = sheetData.Elements<Row>().ToList();
|
||
|
||
if (workbookPart.SharedStringTablePart?.SharedStringTable != null)
|
||
{
|
||
sharedStrings = workbookPart.SharedStringTablePart.SharedStringTable.Descendants<SharedStringItem>().ToArray();
|
||
}
|
||
|
||
if (rows.Count > 0)
|
||
{
|
||
var row = rows[0];
|
||
var cellEnumerator = GetExcelCellEnumerator(row);
|
||
while (cellEnumerator.MoveNext())
|
||
{
|
||
var cell = cellEnumerator.Current;
|
||
var text = ReadExcelCell(cell, sharedStrings).Trim();
|
||
data.Headers.Add(text);
|
||
}
|
||
}
|
||
|
||
// Read the sheet data
|
||
if (rows.Count > 1)
|
||
{
|
||
for (var i = 1; i < rows.Count; i++)
|
||
{
|
||
var dataRow = new List<string>();
|
||
data.DataRows.Add(dataRow);
|
||
var row = rows[i];
|
||
var cellEnumerator = GetExcelCellEnumerator(row);
|
||
while (cellEnumerator.MoveNext())
|
||
{
|
||
var cell = cellEnumerator.Current;
|
||
var text = ReadExcelCell(cell, sharedStrings).Trim();
|
||
dataRow.Add(text);
|
||
}
|
||
}
|
||
}
|
||
|
||
datas.Add(data);
|
||
}
|
||
|
||
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
datas.First().Status.Message = "Unable to open the file";
|
||
return datas;
|
||
}
|
||
|
||
return datas;
|
||
|
||
}
|
||
|
||
public SLExcelData ReadExcel(IFormFile file)
|
||
{
|
||
var data = new SLExcelData();
|
||
|
||
// Check if the file is excel
|
||
if (file.Length <= 0)
|
||
{
|
||
data.Status.Message = "You uploaded an empty file";
|
||
return data;
|
||
}
|
||
|
||
if (file.ContentType != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||
{
|
||
data.Status.Message = "Please upload a valid excel file of version 2007 and above";
|
||
return data;
|
||
}
|
||
|
||
// Open the excel document
|
||
List<Row> rows;
|
||
SharedStringItem[] sharedStrings = new List<SharedStringItem>().ToArray();
|
||
try
|
||
{
|
||
SpreadsheetDocument document;
|
||
using (Stream stream = file.OpenReadStream())
|
||
{
|
||
document = SpreadsheetDocument.Open(stream, false);
|
||
}
|
||
WorkbookPart workbookPart = document.WorkbookPart;
|
||
|
||
var sheets = workbookPart.Workbook.Descendants<Sheet>();
|
||
var sheet = sheets.First();
|
||
data.SheetName = sheet.Name;
|
||
|
||
var workSheet = ((WorksheetPart)workbookPart.GetPartById(sheet.Id)).Worksheet;
|
||
var columns = workSheet.Descendants<Columns>().FirstOrDefault();
|
||
data.ColumnConfigurations = columns;
|
||
|
||
var sheetData = workSheet.Elements<SheetData>().First();
|
||
|
||
|
||
rows = sheetData.Elements<Row>().ToList();
|
||
|
||
if (workbookPart.SharedStringTablePart?.SharedStringTable != null)
|
||
{
|
||
sharedStrings = workbookPart.SharedStringTablePart.SharedStringTable.Descendants<SharedStringItem>().ToArray();
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
data.Status.Message = "Unable to open the file";
|
||
return data;
|
||
}
|
||
|
||
// Read the header
|
||
if (rows.Count > 0)
|
||
{
|
||
var row = rows[0];
|
||
var cellEnumerator = GetExcelCellEnumerator(row);
|
||
while (cellEnumerator.MoveNext())
|
||
{
|
||
var cell = cellEnumerator.Current;
|
||
var text = ReadExcelCell(cell, sharedStrings).Trim();
|
||
data.Headers.Add(text);
|
||
}
|
||
}
|
||
|
||
// Read the sheet data
|
||
if (rows.Count > 1)
|
||
{
|
||
for (var i = 1; i < rows.Count; i++)
|
||
{
|
||
var dataRow = new List<string>();
|
||
data.DataRows.Add(dataRow);
|
||
var row = rows[i];
|
||
var cellEnumerator = GetExcelCellEnumerator(row);
|
||
while (cellEnumerator.MoveNext())
|
||
{
|
||
var cell = cellEnumerator.Current;
|
||
var text = ReadExcelCell(cell, sharedStrings).Trim();
|
||
dataRow.Add(text);
|
||
}
|
||
}
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
public SLExcelData ReadExcel(Stream file, string sheetName, int headerIndex = 0, int? dataIdx = null)
|
||
{
|
||
SLExcelData data = this.ReadRowsAndSharedStrings(file, sheetName, out IList<Row> rows, out SharedStringItem[] sharedStrings);
|
||
|
||
this.FillSLExcelData(data, rows, sharedStrings, headerIndex, dataIdx);
|
||
|
||
return data;
|
||
}
|
||
|
||
public SLExcelData ReadExcel(Stream file, string sheetName, string header, int? dataIdx = null)
|
||
{
|
||
SLExcelData data = this.ReadRowsAndSharedStrings(file, sheetName, out IList<Row> rows, out SharedStringItem[] sharedStrings);
|
||
|
||
int headerIndex = -1;
|
||
|
||
for (int i = 0; i < rows.Count(); i++)
|
||
{
|
||
Row row = rows[i];
|
||
var cellEnumerator = GetExcelCellEnumerator(row);
|
||
while (cellEnumerator.MoveNext())
|
||
{
|
||
var cell = cellEnumerator.Current;
|
||
var text = ReadExcelCell(cell, sharedStrings).Trim();
|
||
if (text.ToLowerEquals(header))
|
||
{
|
||
headerIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
if (headerIndex > -1)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (headerIndex < 0)
|
||
{
|
||
data.Status.Message = $"На листе \"{sheetName}\" не найден заголовок {header}";
|
||
return data;
|
||
}
|
||
|
||
this.FillSLExcelData(data, rows, sharedStrings, headerIndex, dataIdx);
|
||
|
||
return data;
|
||
}
|
||
|
||
public bool IsExcel(Stream file)
|
||
{
|
||
try
|
||
{
|
||
using (var document = SpreadsheetDocument.Open(file, false)) ;
|
||
file.Position = 0;
|
||
return true;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
} |