114 lines
4.3 KiB
C#
114 lines
4.3 KiB
C#
using ClosedXML.Excel;
|
|
|
|
namespace Shared.Excel;
|
|
|
|
public sealed class ClosedXmlWorkbookService
|
|
{
|
|
public IReadOnlyList<string> ReadHeaders(Stream stream)
|
|
{
|
|
using var workbook = new XLWorkbook(stream);
|
|
var sheet = workbook.Worksheets.First();
|
|
var last = sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0;
|
|
if (last == 0) return [];
|
|
|
|
return Enumerable.Range(1, last)
|
|
.Select(i => sheet.Cell(1, i).GetFormattedString().Trim())
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.ToArray();
|
|
}
|
|
|
|
public IEnumerable<ImportRawRow> ReadRows(Stream stream, int maxRows)
|
|
{
|
|
using var workbook = new XLWorkbook(stream);
|
|
var sheet = workbook.Worksheets.First();
|
|
var lastColumn = sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0;
|
|
var lastRow = sheet.LastRowUsed()?.RowNumber() ?? 1;
|
|
var headers = Enumerable.Range(1, lastColumn)
|
|
.Select(i => sheet.Cell(1, i).GetFormattedString().Trim())
|
|
.ToArray();
|
|
|
|
if (lastRow - 1 > maxRows)
|
|
throw new InvalidOperationException($"최대 {maxRows:N0}행까지 업로드할 수 있습니다.");
|
|
|
|
for (var rowNo = 2; rowNo <= lastRow; rowNo++)
|
|
{
|
|
var values = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
|
|
var hasValue = false;
|
|
for (var column = 1; column <= lastColumn; column++)
|
|
{
|
|
var header = headers[column - 1];
|
|
if (string.IsNullOrWhiteSpace(header)) continue;
|
|
var value = sheet.Cell(rowNo, column).GetFormattedString().Trim();
|
|
if (!string.IsNullOrWhiteSpace(value)) hasValue = true;
|
|
values[header] = string.IsNullOrWhiteSpace(value) ? null : value;
|
|
}
|
|
if (hasValue) yield return new ImportRawRow(rowNo, values);
|
|
}
|
|
}
|
|
|
|
public MemoryStream CreateTemplate(ImportDefinition definition)
|
|
{
|
|
var workbook = new XLWorkbook();
|
|
var sheet = workbook.AddWorksheet("업로드");
|
|
var fields = definition.Fields.Where(x => x.Importable).ToArray();
|
|
|
|
for (var i = 0; i < fields.Length; i++)
|
|
{
|
|
var cell = sheet.Cell(1, i + 1);
|
|
cell.Value = fields[i].Label;
|
|
cell.Style.Font.Bold = true;
|
|
cell.Style.Fill.BackgroundColor = XLColor.LightGray;
|
|
cell.Comment.AddText(fields[i].Required ? "필수 입력" : "선택 입력");
|
|
}
|
|
|
|
sheet.SheetView.FreezeRows(1);
|
|
sheet.Columns(1, fields.Length).Width = 18;
|
|
sheet.Row(1).Height = 22;
|
|
sheet.Range(1, 1, 1, fields.Length).SetAutoFilter();
|
|
|
|
var help = workbook.AddWorksheet("필드설명");
|
|
help.Cell("A1").Value = "필드";
|
|
help.Cell("B1").Value = "필수";
|
|
help.Cell("C1").Value = "형식";
|
|
help.Cell("D1").Value = "허용 별칭";
|
|
help.Range("A1:D1").Style.Font.Bold = true;
|
|
for (var i = 0; i < fields.Length; i++)
|
|
{
|
|
var row = i + 2;
|
|
help.Cell(row, 1).Value = fields[i].Label;
|
|
help.Cell(row, 2).Value = fields[i].Required ? "Y" : "N";
|
|
help.Cell(row, 3).Value = fields[i].DataType;
|
|
help.Cell(row, 4).Value = string.Join(", ", fields[i].Aliases);
|
|
}
|
|
help.Columns().AdjustToContents(8, 35);
|
|
|
|
var output = new MemoryStream();
|
|
workbook.SaveAs(output);
|
|
output.Position = 0;
|
|
workbook.Dispose();
|
|
return output;
|
|
}
|
|
|
|
public MemoryStream CreateErrorWorkbook(
|
|
Stream source,
|
|
IReadOnlyDictionary<int, IReadOnlyList<ImportIssue>> issues)
|
|
{
|
|
using var workbook = new XLWorkbook(source);
|
|
var sheet = workbook.Worksheets.First();
|
|
var errorColumn = (sheet.Row(1).LastCellUsed()?.Address.ColumnNumber ?? 0) + 1;
|
|
sheet.Cell(1, errorColumn).Value = "오류사유";
|
|
sheet.Cell(1, errorColumn).Style.Font.Bold = true;
|
|
|
|
foreach (var (rowNo, rowIssues) in issues)
|
|
{
|
|
sheet.Cell(rowNo, errorColumn).Value = string.Join(" | ", rowIssues.Select(x => x.Message));
|
|
}
|
|
sheet.Column(errorColumn).Width = 45;
|
|
|
|
var output = new MemoryStream();
|
|
workbook.SaveAs(output);
|
|
output.Position = 0;
|
|
return output;
|
|
}
|
|
}
|