EPPlus excel read IndexOutOfRangeException - c#

I want to read data from excel (.xlsx or .xls) file
I am using EPPlus
but it give that error IndexOutOfRangeException: Worksheet position out of range.
in this line
OfficeOpenXml.ExcelWorksheet workSheet = package.Workbook.Worksheets[0];
here is my all code.Here is my excel file for redad(http://yazilimsozluk.com/a.xlsx )
.Are there any solution for excel read which works with .xlsx and .xls excel file?
if (Request != null) {
HttpPostedFileBase file = Request.Files["UploadedFile"];
if ((file != null) && (file.ContentLength > 0) && !string.IsNullOrEmpty(file.FileName)) {
string fileName = file.FileName;
string fileContentType = file.ContentType;
byte[] fileBytes = new byte[file.ContentLength];
var data = file.InputStream.Read(fileBytes, 0, Convert.ToInt32(file.ContentLength));
var existingFile = new System.IO.FileInfo(fileName);
var package = new OfficeOpenXml.ExcelPackage(existingFile);
OfficeOpenXml.ExcelWorksheet workSheet = package.Workbook.Worksheets[0];
for (int i = workSheet.Dimension.Start.Column; i <= workSheet.Dimension.End.Column; i++) {
for (int j = workSheet.Dimension.Start.Row; j <= workSheet.Dimension.End.Row; j++) {
object cellValue = workSheet.Cells[i, j].Value;
}
}
}
}

First of all EPPlus can not handle .xls files. See this answer:
Error when trying to read an .xls file using EPPlus
Sample code for reading a file:
var package = new ExcelPackage(new FileInfo("sample.xlsx"));
ExcelWorksheet workSheet = package.Workbook.Worksheets.FirstOrDefault();
for (int i = workSheet.Dimension.Start.Column;
i <= workSheet.Dimension.End.Column;
i++)
{
for (int j = workSheet.Dimension.Start.Row;
j <= workSheet.Dimension.End.Row;
j++)
{
object cellValue = workSheet.Cells[i, j].Value;
}
}

Related

Reading Excel File in Blazor wasm using NPOI

The following code works fine from a .NET Core app running on a PC. The code loads an excel file and reads it using the NPOI library.
public void ReadExcel()
{
DataTable dtTable = new DataTable();
List<string> rowList = new List<string>();
ISheet sheet;
using (var stream = new FileStream("Test.xlsx", FileMode.Open))
{
stream.Position = 0;
XSSFWorkbook xssWorkbook = new XSSFWorkbook(stream);
sheet = xssWorkbook.GetSheetAt(0);
IRow headerRow = sheet.GetRow(0);
int cellCount = headerRow.LastCellNum;
for (int j = 0; j < cellCount; j++)
{
ICell cell = headerRow.GetCell(j);
if (cell == null || string.IsNullOrWhiteSpace(cell.ToString())) continue;
{
dtTable.Columns.Add(cell.ToString());
}
}
for (int i = (sheet.FirstRowNum + 1); i <= sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i);
if (row == null) continue;
if (row.Cells.All(d => d.CellType == CellType.Blank)) continue;
for (int j = row.FirstCellNum; j < cellCount; j++)
{
if (row.GetCell(j) != null)
{
if (!string.IsNullOrEmpty(row.GetCell(j).ToString()) && !string.IsNullOrWhiteSpace(row.GetCell(j).ToString()))
{
rowList.Add(row.GetCell(j).ToString());
}
}
}
if (rowList.Count > 0)
dtTable.Rows.Add(rowList.ToArray());
rowList.Clear();
}
}
return JsonConvert.SerializeObject(dtTable);
}
I want to use this code in my Blazor app to be able to read an Excel file from a browser. I can use the InputFile component to get the file:
<InputFile OnChange="GetFile"/>
The question is how to get the the uploaded file as a stream that I can pass to the ReadExcel function? So it should be something like this:
public async Task GetFile(InputFileChangeEventArgs e) //get excel file
{
stream = e.File.OpenReadStream(); //need a stream here that ReadExcel() can use!
ReadExcel();
}
If I use the above stream in the ReadExcel function instead of the one it has, the code doesnt work. What is the correct way of forming this stream so that ReadExcel can use that instead of the one it has now?
Thanks,
Amjad.
I think the major problem is that a ReadStream is not Seekable (CanSeek == false).
You can copy it to MemoryStream but do keep an eye on the size limits.
public async Task GetFile(InputFileChangeEventArgs e) //get excel file
{
var stream1 = e.File.OpenReadStream(); //need a stream here that ReadExcel() can use!
var stream2 = new MemoryStream();
await stream1.CopyToAsync(stream2);
stream1.Close();
ReadExcel(stream2);
}

Referencing an Excel file, workbook, worksheet in C#

All,
I have been struggling to simply reference an Excel file so that I can extract its data into a new worksheet automatically.
I know this means to create a new Excel file and its items:
Excel.Application oXL;
Excel._Workbook oWB;
Excel._Worksheet oSheet;
Excel.Range oRng;
//Start Excel and get Application object.
oXL = new Excel.Application();
oXL.Visible = true;
//Get a new workbook.
oWB = (Excel._Workbook)(oXL.Workbooks.Add(Missing.Value));
oSheet = (Excel._Worksheet)oWB.ActiveSheet;
But this is where I am lost...I want the user to select another Excel file through a file dialog and then I want to copy data from said file into the new workbook above.
Ex: New file, user selects "MyExcel.csv". How would I reference this so that I can, say, copy Column A into the new worksheet? Whatever works with C#.
Maybe this helps:
Make sure your manage your Nugets and add the reference "Microsoft.Office.Interop.Excel" --> (References (Right click) --> Manage Nuget Packages --> Browse --> "Microsoft.Office.Interop.Excel" --> Install)
Note: This is is a copy paste into the same worksheet. To copy the data into another worksheet from another file just pass the "worksheet" as parameter to the method and paste it there.
public class Excel1
{
private readonly string excelSufix = ".xlsx";
private readonly string excelFilePrefix = "..."; //Where you excel file is located
private string getFilePath(string fileName)
{
return excelFilePrefix + fileName + excelSufix; //returns the file path by the given file name
}
public void CopyPaste(string fileName, int worksheet, string toCopyRange, string whereInsertRange)
{
//Range should look like = "A:C" or "D:F"
var excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Visible = true;
var workBook = excelApp.Workbooks.Open(getFilePath(fileName));
var workSheet = (Microsoft.Office.Interop.Excel.Worksheet)workBook.Sheets[worksheet];
var toCopy = workSheet.Range[toCopyRange];
var whereInsert = workSheet.Range[whereInsertRange];
whereInsert.Insert(Microsoft.Office.Interop.Excel.XlInsertShiftDirection.xlShiftToRight, toCopy.Cut());
}
}
Edit: You could add a constructor to the class where you pass the file Path and some class variables which reference the file, the worksheet etc etc... In this case, you will have independent objects for each file
Based on your description, you want to copy one column from the csv file and paste in the new excel file.
First, I convert the csv file to datatable.
Second, I select the specific column and convert one column to datatable.
Third, I convert the datatable to the new excel file.
You can try the following code.
using System;
using System.Data;
using Excel = Microsoft.Office.Interop.Excel;
class Program
{
static void Main(string[] args)
{
string path = "D:\\t.csv";
DataTable table = ReadCsv(path);
table= new DataView(table).ToTable(false, "Tests");
ExportToExcel(table, "D:\\t.xlsx");
}
public static DataTable ReadCsv(string path)
{
Microsoft.Office.Interop.Excel.Application objXL = null;
Microsoft.Office.Interop.Excel.Workbook objWB = null;
objXL = new Microsoft.Office.Interop.Excel.Application();
objWB = objXL.Workbooks.Open(path);
Microsoft.Office.Interop.Excel.Worksheet objSHT = objWB.Worksheets[1];
int rows = objSHT.UsedRange.Rows.Count;
int cols = objSHT.UsedRange.Columns.Count;
DataTable dt = new DataTable();
int noofrow = 1;
for (int c = 1; c <= cols; c++)
{
string colname = objSHT.Cells[1, c].Text;
dt.Columns.Add(colname);
noofrow = 2;
}
for (int r = noofrow; r <= rows; r++)
{
DataRow dr = dt.NewRow();
for (int c = 1; c <= cols; c++)
{
dr[c - 1] = objSHT.Cells[r, c].Text;
}
dt.Rows.Add(dr);
}
objWB.Close();
objXL.Quit();
return dt;
}
public static void ExportToExcel( DataTable tbl, string excelFilePath = null)
{
try
{
if (tbl == null || tbl.Columns.Count == 0)
throw new Exception("ExportToExcel: Null or empty input table!\n");
// load excel, and create a new workbook
var excelApp = new Excel.Application();
excelApp.Workbooks.Add();
// single worksheet
Excel._Worksheet workSheet = excelApp.ActiveSheet;
// column headings
for (var i = 0; i < tbl.Columns.Count; i++)
{
workSheet.Cells[1, i + 1] = tbl.Columns[i].ColumnName;
}
// rows
for (var i = 0; i < tbl.Rows.Count; i++)
{
// to do: format datetime values before printing
for (var j = 0; j < tbl.Columns.Count; j++)
{
workSheet.Cells[i + 2, j + 1] = tbl.Rows[i][j];
}
}
// check file path
if (!string.IsNullOrEmpty(excelFilePath))
{
try
{
workSheet.SaveAs(excelFilePath);
excelApp.Quit();
Console.WriteLine("Excel file saved!");
}
catch (Exception ex)
{
throw new Exception("ExportToExcel: Excel file could not be saved! Check filepath.\n"
+ ex.Message);
}
}
else
{ // no file path is given
excelApp.Visible = true;
}
}
catch (Exception ex)
{
throw new Exception("ExportToExcel: \n" + ex.Message);
}
}
}
Initial csv file.
The excel file:

Saving DataTable to excel using EPPlus, not saving empty string over previous value

I have an .xlsx file stored on my desktop that my C# program reads from, then it loads each worksheet into datatables that my program uses. When a modification is made to a dataTable, I save to the .xlsx file by first loading the datatable back into the worksheet, and then saving the excelPackage with the modified dataTable information.
The problem is, I sometimes need to overwrite a cell with a blank string, and after saving, the previous value is still there.
It will let me update a value to a space (" "), but I want to save the cell as empty, "", or null.
This is loading my dataTables from the .xlsx
using (var pck = new OfficeOpenXml.ExcelPackage())
{
try
{
using (var stream = File.OpenRead(filePath + "/dataSet.xlsx"))
{
pck.Load(stream);
}
for (int i = 1; i < pck.Workbook.Worksheets.Count; i++)
{
var ws = pck.Workbook.Worksheets[i];
DataTable tbl = new DataTable();
foreach (var firstRowCell in ws.Cells[1, 1, 1, ws.Dimension.End.Column])
{
tbl.Columns.Add(true ? firstRowCell.Text : string.Format("Column {0}", firstRowCell.Start.Column));
}
var startRow = true ? 2 : 1;
for (int rowNum = startRow; rowNum <= ws.Dimension.End.Row; rowNum++)
{
var wsRow = ws.Cells[rowNum, 1, rowNum, ws.Dimension.End.Column];
DataRow row = tbl.Rows.Add();
foreach (var cell in wsRow)
{
row[cell.Start.Column - 1] = cell.Text;
}
}
if (ws.Name == "customerDataTable") { _customerDataTable = tbl; }
else if (ws.Name == "vehicleDataTable") { _vehicleDataTable = tbl; }
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This is saving the dataTable information into worksheet, then to .xlsx
string tableName = "";
if (table == _customerDataTable) { tableName = "customerDataTable"; }
else if (table == _vehicleDataTable) { tableName = "vehicleDataTable"; }
FileInfo file = new FileInfo(filePath + "/dataSet.xlsx");
using (ExcelPackage excelPackage = new ExcelPackage(file))
{
ExcelWorkbook excelWorkBook = excelPackage.Workbook;
ExcelWorksheet excelWorksheet = excelWorkBook.Worksheets[tableName];
excelWorksheet.Cells.LoadFromDataTable(table, true);
excelPackage.Save();
}
I got it to work following VDWWDs post, using the following code:
for (int i = 0; i < table.Rows.Count; i++)
{
for (int j = 0; j < table.Columns.Count; j++)
{
excelWorksheet.Cells[i+2,j+1].Value = table.Rows[i][j];
}
}
//excelWorksheet.Cells.LoadFromDataTable(table, true);
I replaced the LoadFromTable() line with a nested For loop. Although it functionally works, I'm going to be using this method a lot throughout my program, and this solution seems a bit bulky compared to EPPlus LoadFromTable method. I'm thinking there has to be a better way...

how to get range in excel sheet using openxml and c#

I have to read Excel(.xlsx) file. I am using OpenXML NuGet pkg.
I am trying to get the range(xlRange), so I can fetch the data from a particular cell. But not getting success. This code is created in console environment.
static void Main(string[] args)
{
bool flagDataError = false;
string sDuplicateRows = string.Empty;
string sCreatedRows = string.Empty;
string sFileTypeError = string.Empty;
string filepath = #"C:\test.xlsx";
var fileName = Path.GetFileName(filepath);
var fileExtension = Path.GetExtension(filepath);
if ((fileExtension != ".xlsx") && (fileExtension != ".xls"))
sFileTypeError = "Invalid file. \n\nPlease browse a correct Excel file to upload.";
else
{
using (SpreadsheetDocument doc = SpreadsheetDocument.Open(filepath, false))
{
WorkbookPart workbookPart = doc.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
List<AxisData> data = new List<AxisData>();
for (int i = 5; i <= 16; i++)
{
for (int j = 2; j <= 13; j++)
{
AxisData axisDetails = new AxisData();
axisDetails.ProductCode = "AXIS " + xlRange.Cells[i, 1].Value;
axisDetails.ContractPeriod = Convert.ToDateTime(xlRange.Cells[4, j].Value);
axisDetails.SettlePrice = Convert.ToDecimal(xlRange.Cells[i, j].Value);
axisDetails.EffectiveStartDate = efectiveStartDate;
}
}
}
}
}
}
}
IEnumerable<Row> ShRows = sheetData.Elements<Row>();
var _RCount = SHRows.Count();

How to Read the Uploaded Excel File using NPOI with out storing in the server or in the Project

My sample Code is below using the NPOI.dll. I can read the EXCEL File (i.e. Excel would be stored in the system like D:/Jamal/Test.xls. Then the dll is easily reading the content, but I need to read the uploaded Excel file without storing it in any place before.The HTTPPOSTEDFILEBASE excelfile has the value for the Excel file but I need to know how to read it using NPOI dlls
public List<string> SendInvitesExcelFile1(List<String> CorrectMailIDs,
ListInvites Invites, HttpPostedFileBase excelfile)
{
List<string> mailids = new List<string>();
//string filename = (excelfile.FileName).ToString();
HSSFWorkbook hssfwb;
// using (FileStream file = new FileStream(#"D:\test.xls", FileMode.Open, FileAccess.Read))
using (FileStream file = new FileStream(excelFile.FileName, FileMode.Open, FileAccess.Read))
{
hssfwb = new HSSFWorkbook(file);
}
Sheet sheet = hssfwb.GetSheet("sheet1");
for (int row = 0; row <= sheet.LastRowNum; row++)
{
if (sheet.GetRow(row) != null) //null is when the row only contains empty cells
{
mailids.Add(sheet.GetRow(row).GetCell(0).ToString());
}
}
return mailids;
I came across the same problem and I solved it using Inpustream. I am pasting the code for your reference.
[HttpPost]
public DataTable PostValues(HttpPostedFileBase file)
{
ISheet sheet;
string filename = Path.GetFileName(Server.MapPath(file.FileName));
var fileExt = Path.GetExtension(filename);
if (fileExt == ".xls")
{
HSSFWorkbook hssfwb = new HSSFWorkbook(file.InputStream);
sheet = hssfwb.GetSheetAt(0);
}
else
{
XSSFWorkbook hssfwb = new XSSFWorkbook(file.InputStream);
sheet = hssfwb.GetSheetAt(0);
}
DataTable table = new DataTable();
IRow headerRow = sheet.GetRow(0);
int cellCount = headerRow.LastCellNum;
for (int i = headerRow.FirstCellNum; i < cellCount; i++)
{
DataColumn column = new DataColumn(headerRow.GetCell(i).StringCellValue);
table.Columns.Add(column);
}
int rowCount = sheet.LastRowNum;
for (int i = (sheet.FirstRowNum); i < sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i);
DataRow dataRow = table.NewRow();
for (int j = row.FirstCellNum; j < cellCount; j++)
{
if (row.GetCell(j) != null)
{
dataRow[j] = row.GetCell(j).ToString();
}
}
table.Rows.Add(dataRow);
}
return table;
}
You can use a MemoryStream as well, so you should be able to get the byte array repsonse and open the spreadsheet. I'm unsure if the WorkbookFactory will detect the file type from the MemoryStream, so you may need to instruct the users to use the format that you require.
Here is how I use a MemoryStream to return a xls from a dot net core controller without ever storing it as a file.
IWorkbook wb = new HSSFWorkbook();
//Edit workbook object here
MemoryStream m = new MemoryStream();
wb.Write(m);
var byteArray = m.ToArray();
return new FileContentResult(byteArray, "application/vnd.ms-excel");

Categories