EPPlus:Cannot access a closed Stream when copy existing excel worksheet - c#

I'm currently using EPPlus and right now I'm trying to copy existing worksheet from an excel file and add the copied worksheet to another excel file. But then, Ive encounter this error:
Full Stack Trace
System.ObjectDisposedException: Cannot access a closed Stream.
at System.IO.MemoryStream.Seek(Int64 offset, SeekOrigin loc)
at OfficeOpenXml.Packaging.ZipPackagePart.GetStream(FileMode fileMode, FileAccess fileAccess)
at OfficeOpenXml.Packaging.ZipPackagePart.WriteZip(ZipOutputStream os)
at OfficeOpenXml.Packaging.ZipPackage.Save(Stream stream)
at OfficeOpenXml.ExcelPackage.Save()
at OfficeOpenXml.ExcelPackage.SaveAs(Stream OutputStream)
Excel Helper
public byte[] ExportStraightBoxClampPreview(PreviewInputDto input)
{
//the path of the file
string filePath = #"C:\Preview\WeldedStraightBox.xlsx";
string filePath1 = #"C:\Preview\Strongback.xlsx";
//create a fileinfo object of an excel file on the disk
FileInfo file = new FileInfo(filePath);
FileInfo file1 = new FileInfo(filePath1);
FileInfo file2 = new FileInfo(filePath2);
//create a new Excel package from the file
using (ExcelPackage strongBack = new ExcelPackage(file1))
using (ExcelPackage excelPackage = new ExcelPackage(file))
{
ExcelWorksheet wsSrc = strongBack.Workbook.Worksheets.First();
ExcelWorksheet wsDest = excelPackage.Workbook.Worksheets[wsSrc.Name] ?? excelPackage.Workbook.Worksheets.Add(wsSrc.Name, wsSrc);
** error here **------> return excelPackage.GetAsByteArray();
}
Is it because I do not use Memory Stream? But then If I manually copied using looping for each row and column, it is working:
another method
ExcelWorksheet wsSrc = strongBack.Workbook.Worksheets.First();
ExcelWorksheet wsDest = excelPackage.Workbook.Worksheets[wsSrc.Name] ?? excelPackage.Workbook.Worksheets.Add(wsSrc.Name);
for (var r = 1; r <= wsSrc.Dimension.Rows; r++)
{
for (var c = 1; c <= wsSrc.Dimension.Columns; c++)
{
var cellSrc = wsSrc.Cells[r, c];
var cellDest = wsDest.Cells[r, c];
// Copy value
cellDest.Value = cellSrc.Value;
// Copy cell properties
cellDest.Style.Numberformat = cellSrc.Style.Numberformat;
cellDest.Style.Font.Bold = cellSrc.Style.Font.Bold;
// TODO... Add any additional properties that you may want to copy over
}
}
I'm really sorry for the lack of knowledge about this. I'm really appreciate any help.

Related

C# Problem opening excel file using `OpenXml` from `byte[]`

I have a problem when saving and opening a file with OpenXml library. Here is my code:
public static void SaveExcel(List<Dictionary<string, object>> listData, List<string> entityTypes, string appName)
{
using (var ms = new MemoryStream())
using (var excel = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook))
{
var workBookPart = excel.AddWorkbookPart();
workBookPart.Workbook = new Workbook();
var workSheetPart = workBookPart.AddNewPart<WorksheetPart>();
var workSheetData = new SheetData();
workSheetPart.Worksheet = new Worksheet(workSheetData);
var sheets = workBookPart.Workbook.AppendChild(new Sheets());
var index = 1;
foreach (var entityType in entityTypes)
{
var sheet = new Sheet
{
Id = excel.WorkbookPart.GetIdOfPart(workSheetPart),
SheetId = 1U,
Name = entityType
};
sheets.AppendChild(sheet);
}
workBookPart.Workbook.Save(ms);
File.WriteAllBytes("D:/nothing123.xlsx", ms.ToArray());
}
}
I am pretty sure I did the right thing though I have this error when opening the file:
Excel cannot be opened the file 'nothing123.xlsx' because the file format or file extension is not valid. Verify that the file has not been corrupted and the file extension matches the format of the file.
Any idea whats going on with my code?
I don't know if this is relevant to the tag but I made up my mind to use ClosedXml library since it's much easier to use than OpenXml. I can easily create a DataTable and create an Excel file out of the DataTable which is very convenient. Here is my quick sample code:
Sample Data Table
public DataTable getData() {
DataTable dt = new DataTable();
dt.TableName = "SheetName1";
dt.Columns.Add("FirstName");
dt.Columns.Add("LastName");
var row = dt.NewRow();
row["FirstName"] = "Alvin";
row["LastName"] = "Quezon";
dt.Rows.Add(row);
return dt;
}
Sample Code to Excel to Byte Array
public static byte[] GetExcelBytes(DataTable dataTable)
{
using (var ms = new MemoryStream())
using (var workBook = new XLWorkbook())
{
workBook.Worksheets.Add(dataTable);
workBook.SaveAs(ms);
return ms.ToArray();
}
}
I didn't get any issue when opening the file and with superb minimal code usage.
Hopefully this will help anyone would like to use this in the future.

ClosedXML - charts missing after saving

I'm using ClosedXML to change the values of some cells in a XLSX file. These cells are used to generate a chart on another sheet.
I'm not touching the chart at all with ClosedXML. In fact, I'm not even touching the sheet that contains it, I'm just changing the values of some data cells, like this:
var workbook = new XLWorkbook(#"C:\path\to\my\file.xlsx");
var dataWorksheet = workbook.Worksheet("data");
var cell = dataWorksheet.Cell(1, 2);
cell.Value = 30;
workbook.Save();
However, when I re-open the file in Excel, the chart that was on the other sheet (which I haven't touched at all) simply disappears, leaving the sheet empty.
I know ClosedXML doesn't support the CREATION of charts, but is there a way to avoid LOSING them when saving?
Unfortunately that is a limitation on ClosedXML there are a few "chart" requests on the project page:
https://github.com/ClosedXML/ClosedXML/issues?utf8=%E2%9C%93&q=is%3Aissue+chart
But fear not the XLSX format is very simple! it's just a bunch of files all zipped.
We can still use ClosedXML to edit that Worksheet save it as a temp file and then replace the sheet on the original file, here is my code:
using ClosedXML.Excel;
using Ionic.Zip;
using System.IO;
using System.Linq;
namespace ExcelTest
{
class Program
{
static void Main(string[] args)
{
string file = #"..\..\file.xlsx";
ClosedXMLTest(file);
}
static void ClosedXMLTest(string file)
{
string outFile = "fileClosedXML.xlsx";
var workbook = new XLWorkbook(file);
var dataWorksheet = workbook.Worksheet("data");
for (int i = 1; i < 10; i++)
dataWorksheet.Cell(i, i).Value = i;
workbook.SaveAs(outFile);
ReplaceSheet(outFile, file, "xl/worksheets/sheet1.xml");
}
static void ReplaceSheet(string outputFile, string inputFile, string sheetName)
{
using (var ozip = new ZipFile(outputFile))
using (var izip = new ZipFile(inputFile))
{
var osheet = ozip.Entries.Where(x => x.FileName == sheetName).FirstOrDefault();
var tempS = new MemoryStream();
osheet.Extract(tempS);
var isheet = izip.Entries.Where(x => x.FileName == sheetName).FirstOrDefault();
izip.RemoveEntry(isheet);
izip.AddEntry(isheet.FileName, tempS.ToArray());
izip.Save();
}
}
}
}
I have the full project here:
https://github.com/heldersepu/csharp-proj/tree/master/ExcelTest

C# EPPlus merge Excel Files

I want to merge multiple Excel files with EPPlus in C#.
I did the following:
using (MemoryStream protocolStream = new MemoryStream())
{
ExcelPackage pck = new ExcelPackage();
HashSet<string> wsNames = new HashSet<string>();
foreach (var file in files)
{
ExcelPackage copyPck = new ExcelPackage(new FileInfo(file));
foreach (var ws in copyPck.Workbook.Worksheets)
{
string name = ws.Name;
int i = 1;
while (!wsNames.Add(ws.Name))
name = ws.Name + i++;
ws.Name = name;
var copiedws = pck.Workbook.Worksheets.Add(name);
copiedws.WorksheetXml.LoadXml(ws.WorksheetXml.DocumentElement.OuterXml);
}
}
pck.SaveAs(protocolStream);
protocolStream.Position = 0;
using (FileStream fs = new FileStream(resultFile, FileMode.Create))
protocolStream.CopyTo(fs);
}
But I get the following error in pck.SaveAs(protocolStream):
System.ArgumentOutOfRangeException
in
System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument
argument, ExceptionResource resource) in
System.Collections.Generic.List1.get_Item(Int32 index) in
OfficeOpenXml.ExcelStyleCollection1.get_Item(Int32 PositionID)
I also tried it with the Worksheet.Copy method, but I lose the styling with it.
Here is an example of merging several files into one by coping all worksheets from source excel files.
var files = new string[] { #"P:\second.xlsx", #"P:\second.xlsx" };
var resultFile = #"P:\result.xlsx";
ExcelPackage masterPackage = new ExcelPackage(new FileInfo(#"P:\first.xlsx"));
foreach (var file in files)
{
ExcelPackage pckg = new ExcelPackage(new FileInfo(file));
foreach (var sheet in pckg.Workbook.Worksheets)
{
//check name of worksheet, in case that worksheet with same name already exist exception will be thrown by EPPlus
string workSheetName = sheet.Name;
foreach (var masterSheet in masterPackage.Workbook.Worksheets)
{
if (sheet.Name == masterSheet.Name)
{
workSheetName = string.Format("{0}_{1}", workSheetName, DateTime.Now.ToString("yyyyMMddhhssmmm"));
}
}
//add new sheet
masterPackage.Workbook.Worksheets.Add(workSheetName, sheet);
}
}
masterPackage.SaveAs(new FileInfo(resultFile));

NPOI xls: calculated end index (x) is out of allowable range (y..y+2)

I always get an exception when writing to newly created file. I get this exception even if provided sheet is empty. Workbook without sheets saves without a problem.
This exception shows for an empty sheet.
Additional information: calculated end index (1932) is out of
allowable range (1908..1910)
I'm using NOPI.dll ver.2.1.3.1 http://npoi.codeplex.com/
File I try to create is .xls (Excel 97-2003)
filePath in code bolow has a value #"C:\TB\Report.xls"
My code
public void Generate()
{
HSSFWorkbook newWorkBook = new HSSFWorkbook();
ISheet newSheet = newWorkBook.CreateSheet("Main");
newWorkBook.Add(newSheet);
using (FileStream fileOut = new FileStream(filePath, FileMode.Create))
{
newWorkBook.Write(fileOut);
}
}
File is created but it's empty.
When creating xls with sheet that has rows and columns the result is the same except the numbers in exception are higher.
The problem was using ISheet instead of HSSFSheet. Example shown in the documentation used ISheet but it was incorrect.
Correct code:
public void Generate()
{
HSSFWorkbook newWorkBook = new HSSFWorkbook();
HSSFSheet newSheet = (HSSFSheet)newWorkBook.CreateSheet("Main");
var headerRow = newSheet.CreateRow(0);
var headerCell = headerRow.CreateCell(0);
headerCell.SetCellValue("Something");
using (FileStream fileOut = new FileStream(filePath, FileMode.Create))
{
newWorkBook.Write(fileOut);
}
}

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