I wrote this code below
string filePath = #"C:\report_data.xlsx";
// Saves the file via a FileInfo
var file = new FileInfo(filePath);
// Creates the package and make sure you wrap it in a using statement
using (var package = new ExcelPackage(file))
{
// Adds a new worksheet to the empty workbook
OfficeOpenXml.ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Report System - " + DateTime.Now.ToShortDateString());
// Starts to get data from database
for (int row = 1; row < 10; row++)
{
// Writes data from sql database to excel's columns
for (int col = 1; col < 10; col++)
{
worksheet.Cells[row, col].Value = row * col;
}// Ends writing data from sql database to excel's columns
}// Ends getting data from database
// Saves new workbook and we are done!
package.Save();
}
When i opened the file , it had nothing and the page was blank. But when i saw the file size , it increased . Why was the page blank while the file size increased and how can i write data to it ?
Remark : When i tried deleting the existing file and run a program, the program generated the file and the file had values displayed in the columns . On the other hand , i tried creating file first and then run the program and the result was blank like i said above .
I found out the answer . I just changed the line at
OfficeOpenXml.ExcelWorksheet worksheet =
package.Workbook.Worksheets.Add("Report System - " +
DateTime.Now.ToShortDateString());
to
OfficeOpenXml.ExcelWorksheet worksheet =
package.Workbook.Worksheets["Sheet1"];
and it worked !!.
Because Sheet1 is the default name of worksheet and it existed in my file at line:
string filePath = #"C:\report_data.xlsx";
Actually, in this case, package.Workbook.Worksheets.Add means to add the worksheet's name and I found that it didn't have to add the worksheet's name because it already exists.
Related
I have some code that is suppose to enter some values into several excel workbooks. Right now the program doesn't even put any values into the workbooks and only saves them. Even like this i get this error when opening the files: Excel cannot open the file **.xlsm because the file format or file extension is not valid. Verify that the file has been corrupted and that the file extension matches the format of the file.
I have writen many programs that work with excel files and never had this problem. In the code you can see that i basically just go through a for loop and save the file.
try
{
fileInfo = new FileInfo(Path.GetDirectoryName(Application.StartupPath) + '\\' + partners[partner].partnerName + #"\PDP_ExSumm_" + partners[partner].partnerName + ".xlsm");
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[1];
for (int cell = 0; cell < ExSummCells.Count; cell++)
{
if (ExSummCells[cell] != "")
{
// worksheet.Cells[ExSummCells[cell]].Value = partners[partner].exSummData[partner];
}
excelPackage.Save();
}
}
A little background on problem:
We have an ASP.NET MVC5 Application where we use FlexMonster to show the data in grid. The data source is a stored procedure that brings all the data into the UI grid, and once user clicks on export button, it exports the report to Excel. However, in some cases export to excel is failing.
Some of the data has some invalid characters, and it is not possible/feasible to fix the source as suggested here
My approach so far:
EPPlus library fails on initializing the workbook as the input excel file contains some invalid XML characters. I could find that the file is dumped with some invalid character in it. I looked into the possible approaches .
Firstly, I identified the problematic character in the excel file. I first tried to replace the invalid character with blank space manually using Notepad++ and the EPPlus could successfully read the file.
Now using the approaches given in other SO thread here and here, I replaced all possible occurrences of invalid chars. I am using at the moment
XmlConvert.IsXmlChar
method to find out the problematic XML character and replacing with blank space.
I created a sample program where I am trying to work on the problematic excel sheet.
//in main method
String readFile = File.ReadAllText(filePath);
string content = RemoveInvalidXmlChars(readFile);
File.WriteAllText(filePath, content);
//removal of invalid characters
static string RemoveInvalidXmlChars(string inputText)
{
StringBuilder withoutInvalidXmlCharsBuilder = new StringBuilder();
int firstOccurenceOfRealData = inputText.IndexOf("<t>");
int lastOccurenceOfRealData = inputText.LastIndexOf("</t>");
if (firstOccurenceOfRealData < 0 ||
lastOccurenceOfRealData < 0 ||
firstOccurenceOfRealData > lastOccurenceOfRealData)
return inputText;
withoutInvalidXmlCharsBuilder.Append(inputText.Substring(0, firstOccurenceOfRealData));
int remaining = lastOccurenceOfRealData - firstOccurenceOfRealData;
string textToCheckFor = inputText.Substring(firstOccurenceOfRealData, remaining);
foreach (char c in textToCheckFor)
{
withoutInvalidXmlCharsBuilder.Append((XmlConvert.IsXmlChar(c)) ? c : ' ');
}
withoutInvalidXmlCharsBuilder.Append(inputText.Substring(lastOccurenceOfRealData));
return withoutInvalidXmlCharsBuilder.ToString();
}
If I replaces the problematic character manually using notepad++, then the file opens fine in MSExcel. The above mentioned code successfully replaces the same invalid character and writes the content back to the file. However, when I try to open the excel file using MS Excel, it throws an error saying that file may have been corrupted and no content is displayed (snapshots below). Moreover, Following code
var excelPackage = new ExcelPackage(new FileInfo(filePath));
on the file that I updated via Notepad++, throws following exception
"CRC error: the file being extracted appears to be corrupted. Expected 0x7478AABE, Actual 0xE9191E00"}
My Questions:
Is my approach to modify content this way correct?
If yes, How can I write updated string to an Excel file?
If my approach is wrong then, How can I proceed to get rid of invalid XML chars?
Errors shown on opening file (without invalid XML char):
First Pop up
When I click on yes
Thanks in advance !
It does sounds like a binary (presumable XLSX) file based on your last comment. To confirm, open the file created by the FlexMonster with 7zip. If it opens properly and you see a bunch of XML files in folders, its a XLSX.
In that case, a search/replace on a binary file sounds like a very bad idea. It might work on the XML parts but might also replace legit chars in other parts. I think the better approach would be to do as #PanagiotisKanavos suggests and use ZipArchive. But you have to do rebuild it in the right order otherwise Excel complains. Similar to how it was done here https://stackoverflow.com/a/33312038/1324284, you could do something like this:
public static void ReplaceXmlString(this ZipArchive xlsxZip, FileInfo outFile, string oldString, string newstring)
{
using (var outStream = outFile.Open(FileMode.Create, FileAccess.ReadWrite))
using (var copiedzip = new ZipArchive(outStream, ZipArchiveMode.Update))
{
//Go though each file in the zip one by one and copy over to the new file - entries need to be in order
foreach (var entry in xlsxZip.Entries)
{
var newentry = copiedzip.CreateEntry(entry.FullName);
var newstream = newentry.Open();
var orgstream = entry.Open();
//Copy non-xml files over
if (!entry.Name.EndsWith(".xml"))
{
orgstream.CopyTo(newstream);
}
else
{
//Load the xml document to manipulate
var xdoc = new XmlDocument();
xdoc.Load(orgstream);
var xml = xdoc.OuterXml.Replace(oldString, newstring);
xdoc = new XmlDocument();
xdoc.LoadXml(xml);
xdoc.Save(newstream);
}
orgstream.Close();
newstream.Flush();
newstream.Close();
}
}
}
When it is used like this:
[TestMethod]
public void ReplaceXmlTest()
{
var datatable = new DataTable("tblData");
datatable.Columns.AddRange(new[]
{
new DataColumn("Col1", typeof (int)),
new DataColumn("Col2", typeof (int)),
new DataColumn("Col3", typeof (string))
});
for (var i = 0; i < 10; i++)
{
var row = datatable.NewRow();
row[0] = i;
row[1] = i * 10;
row[2] = i % 2 == 0 ? "ABCD" : "AXCD";
datatable.Rows.Add(row);
}
using (var pck = new ExcelPackage())
{
var workbook = pck.Workbook;
var worksheet = workbook.Worksheets.Add("source");
worksheet.Cells.LoadFromDataTable(datatable, true);
worksheet.Tables.Add(worksheet.Cells["A1:C11"], "Table1");
//Now similulate the copy/open of the excel file into a zip archive
using (var orginalzip = new ZipArchive(new MemoryStream(pck.GetAsByteArray()), ZipArchiveMode.Read))
{
var fi = new FileInfo(#"c:\temp\ReplaceXmlTest.xlsx");
if (fi.Exists)
fi.Delete();
orginalzip.ReplaceXmlString(fi, "AXCD", "REPLACED!!");
}
}
}
Gives this:
Just keep in mind that this is completely brute force. Anything you can do to make the file filter smarter rather then simply doing ALL xml files would be a very good thing. Maybe limit it to the SharedString.xml file if that is where the problem lies or in the xml files in the worksheet folders. Hard to say without knowing more about the data.
Using EPPlus I want to add a new sheet to an Excel file but I do not want to delete the existing sheets in the file if any, and I want to insert it as the first sheet in the file.
Here is what I have written for a quick test but it deletes all the existing sheets:
using (ExcelPackage p = new ExcelPackage())
{
p.Workbook.Worksheets.Add("HubaHuba");
p.Workbook.Worksheets.MoveToStart("HubaHuba");
ExcelWorksheet ws = p.Workbook.Worksheets[1];
ws.Name = "HubaHuba";
var cell = ws.Cells[1, 1];
cell.Value = "dfsdfsdfsd";
cell = ws.Cells[1, 2];
cell.Value = "347895y5 Oh";
Byte[] bin = p.GetAsByteArray();
File.WriteAllBytes(path,bin);
}
using (ExcelPackage excelEngine = new ExcelPackage())
{
excelEngine.Workbook.Worksheets.Add("sheet1");
excelEngine.Workbook.Worksheets.Add("sheet2");
excelEngine.Workbook.Worksheets.Add("sheet3");
String myFile= "c:\....\xx.xlsx";
excelEngine.SaveAs();
}
When you use Add the sheet is added at the and of current file sheets.
If you want to add in a specific position use this function:
excelEngine.Workbook.Worksheets.Add("sheet0");
excelEngine.Workbook.Worksheets.MoveBefore(4, 1);
sheet0 is added at the and in 4th position and you move it in first position with the previous code.
That's because you're rewriting the file with command File.WriteAllBytes. Instead you should just call p.Save() and ExcelPackage needs to use the constructor that accepts the file path. Then it will work.
I have C# app for deleting first few rows from excel and then format file to .csv, but now i got not .xlsx but .xlsm and i cant find how to work with, i cant even load data from columns. Its some report file from SAP and i dont find any macro inside. I tried something like this
/* Load Excel File */
Excel.Application excelApp = new Excel.Application();
string workbookPath = #"file.xlsm";
Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(workbookPath, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
/* Load worksheets collection */
Excel.Sheets excelSheets = excelWorkbook.Worksheets;
/* Select first worksheet */
Excel.Worksheet excelWorksheet = (Excel.Worksheet)excelSheets[1];
/* Deleting first 87 Rows */
Excel.Range range = excelWorksheet.get_Range("1:87").EntireRow;
range.Delete(Excel.XlDeleteShiftDirection.xlShiftUp);
/* Save File */
excelWorkbook.SaveAs(#"out_file.xlsm");
excelWorkbook.Close(false);
excelApp.Application.Quit();
/* Release COM objects otherwise Excel remain running */
releaseObject(range);
releaseObject(excelWorkbook);
releaseObject(excelWorksheet);
releaseObject(excelApp);
This work with .xlsx extension (it will delete rows and save it under another name) but not with .xlsm (program run successfully but it dont delete data). Even if i manually excel file save as .xlsx and run program on that file it dont work, but if i manually copy paste data to another .xlsx and run program on that file it work, i dont get it. How can i rewrite this program to delete rows from .xlsm files ? Please help, thank you.
Thanks to Christian Sauer, the EPPLUS.dll worked.
Step 1
Solution Explorer > Project Name > Add > Reference > Browse to EPPLUS.dll
Step 2
using OfficeOpenXml;
using OfficeOpenXml.Style;
using System.IO;
Step 3 (delete rows range)
using (var p = new ExcelPackage(new FileInfo(#"file.xlsm")))
{
var sheet = p.Workbook.Worksheets["Sheet1"];
sheet.DeleteRow(1, 87);
p.SaveAs(new FileInfo(#"output.xlsm"));
}
)
Step 4 (export .xlsm to .csv)
Insert Code between these lines
sheet.DeleteRow(1, 87);
====>[HERE]
p.SaveAs(new FileInfo(#"output.xlsm"));
/* Code placed to [HERE] placeholder */
using (var writer = File.CreateText(#"output.csv"))
{
var rowCount = sheet.Dimension.End.Row;
var columnCount = sheet.Dimension.End.Column;
for (var r = 1; r <= rowCount; r++)
{
for (var c = 1; c <= columnCount; c++)
{
writer.Write(sheet.Cells[r, c].Value);
writer.Write(";");
}
writer.WriteLine();
}
}
I am having some issues with the following:
I give my method a dataset and it is supposed to throw the information into an excel file, which is a template of the formatting I desire. The excel file i created has a header, some filters and things of the sort, and I set my method to populate the file AFTER the header, etc but the problem is, when I do that, I lose all the formatting i had on the template. I am using this class http://www.codeproject.com/KB/office/biffcsharp.aspx . Im not sure, it may be that the format for the implementation of the class is a real simple one or that it overwrites all the information I had.
my method looks like this, using the class on the link above :
public void PopularSheet()
{
string filename = "C:\\test" + System.Web.HttpContext.Current.Session["SYSTEMCLIENTID"].ToString()+ System.Web.HttpContext.Current.Session["SYSTEMUSERTYPEID"].ToString()+ System.Web.HttpContext.Current.Session["CLIENTID"].ToString()+".xls";
File.Copy("C:\\test.xls", filename);
FileStream stream = new FileStream(filename, FileMode.OpenOrCreate);
ExcelWriter writer = new ExcelWriter(stream);
DataSet ds = GetDataSet();
writer.BeginWrite();
int jValue = ds.Tables[0].Columns.Count;
int iValue = ds.Tables[0].Rows.Count;
// Passa os dados do dataset para a planilha
for (int i = 0; i < iValue; i++)
{
// LĂȘ todas as colunas da linha i
for (int j = 0; j < jValue; j++)
{
writer.WriteCell(i+2, j, ds.Tables[0].Rows[i][j].ToString());
}
}
writer.EndWrite();
stream.Close();
}
I also tried using an excel library,http://www.carlosag.net/tools/excelxmlwriter/,but i think in order to LOAD a file (so that i can insert the information i need into it) i need to load a xml file, which is impossible!
Another library I used presented a problem when saving, I was able to edit the worksheet and then when i saved and opened the excel file that was generated thru the code, it would come out empty .
I cannot use anything that will force me to install excel, which is why i am trying these alternatives. Are there any suggestions to what I could do?
What i need to do :
Load an existing excel file as "template"
Throw a dataset into the file
Save the file with the information that i threw with the template format
There is a library called ClosedXML which is useful for creating openxml file.
Quick thought: can you use a .csv instead of a standard .xls file?
If so, you can easily fill your table.