InvalidOperationException while reading Excel file with NPOI - c#

I have simple piece of code
public JsonResult ParseExcel(HttpPostedFileBase file)
{
var uploadedFileContent = file.InputStream;
if (POIXMLDocument.HasOOXMLHeader(uploadedFileContent))
{
//xlsx
var workbook = new XSSFWorkbook(uploadedFileContent);
var sheet = workbook.GetSheetAt(0);
}
return new JsonResult{};
}
So then I try to create a workbook using NPOI with uploaded Excel file, I get error: Exception details:
Message: The hyperlink for cell G33 references relation rId5, but that didn't exist!
I found out that this happens because G33 cell has a hyperlink, which is like this: xxx#yyy.com'. It has a single quote in the end. If I remove single quote, everything works as expected. Is there any other way to solve this problem rather that editing Excel file and removing single quote?
I tried using WorkbookFactory.Create and specifying second param as ImportOption.TextOnly. I tried creating OPCPackage object first and then passing it as a param. However, I get the same message.

As passed file to XSSFWorkbook constructor, corrupts the whole thing, I did not find any other solution then to switch to a different library.

Related

Creating a CSV with c# that doesnt get "corrupted" by excel

I'm generating the head of different CSV files so my users can see the format
var output = new MemoryStream()
var writer = new StreamWriter(output, Encoding.UTF8);
//this function gets the row depending of the enumerator name in this format a;b;c;d
var header = ModelosCsv.GetCsvByEnum("HeadRowFileLoad");
writer.WriteLine(header);
writer.Flush();
output.Position = 0;
return File(output, "application/csv", "format.csv");
The code is creating the CSV correctly but if they open the CSV with excel and save it, excel will overwrite all the ";" for triple spaces.
If I edit the result with notepad++ and put back the ";" excel won't do it again.
I have opened both archives with excel and clicked "save as", the first one (freshly generated by c#) is set as default as "text archive" the second one (edited by notepad++) is set as CSV.
Am I missing something code?
How could I do to stop excel messing up my archives?
I found the solution in this answered question
Export to CSV using MVC, C# and jQuery
var header = ModelosCsv.GetCsvByEnum("HeadRowFileLoad");
return File(new System.Text.UTF8Encoding().GetBytes(header), "application/csv", "format.csv");
This way Excel identifies the archive as CSV and editing wont break the format.

Appending new cells breaks the excel file

This might be the duplicate as many posts refer to this kind of issues but I could not find an exact answer to this case.
So, what I have is an Excel file where cell "E4" contains a formula "=C4+D4". All other cells are empty, which means I cannot search or get them via OpenXml, simply because they do not exist in Xml. So in order to fill the cells "C4" and "D4" I have to create them (like this:)
var cell = new Cell(){
CellReference = new StringValue("C4"),
DataType = new EnumValue<CellValues>(CellValues.Number),
CellValue = new CellValue("123")
}
the same for cell "D4" and then append these cells to the row
row.Append(cell);
After I open the excel file it show an error "Excel found unreadable content in file.xlsx. Do you want to recover the contents of this workbook? If you trust the source of this workbook, click Yes."
I checked and when there is no formula in excel file the cells are appended correctly.
So my question is, using OpenXml how do I append cells in Excel file, which contains formula, so that not to break the file?
So the XML is malformed and corrupted the has file, hence the error:
Excel found unreadable content in file
To troubleshoot and fix this I suggest you compare the manually created xlsx to the programmatically created xlsx.
To do this you rename both files extensions from XLSX to ZIP > extract them to different folders > use a WinDiff tool (or better the "OpenXML SDK Productivity Tool") to compare the files.
Once you find how its malformed change your code to try and fix it up.
I solved my problem using EPPLUS
using (ExcelPackage excelPackage = new ExcelPackage(fileStream))
{
ExcelWorkbook excelWorkBook = excelPackage.Workbook;
ExcelWorksheet excelWorksheet = excelWorkBook.Worksheets.First();
excelWorksheet.Cells["C4"].Value = "123";
excelWorksheet.Cells["D4"].Value = "321";
excelPackage.SaveAs(memoryStream);
}

EPPlus worksheet always throws Exception at first attempt, then works at the second attempt

This snippet of code is from a larger function in an MVC controller where a form with a file is posted. The inputStream comes from the uploaded file:
var excelFile = new ExcelPackage(inputStream.BaseStream);
ExcelWorksheet worksheet;
try
{
worksheet = excelFile.Workbook.Worksheets["Products"];
}
catch (Exception)
{
// Second time always works?
worksheet = excelFile.Workbook.Worksheets["Products"];
}
It always crashes when trying it at first and then it will work the second time. The exception I get is "An item with the same key has already been added.". The second attempt never fails.
Sure this is working code, but people might be tempted to poke fun at me for checking this in ;)
Version:
EPPlus.dll, v3.1.3.0
I was having this same problem today. Very frustrating! I even looked into the source code of EPPlus to see what was going on, but it didn't really help.
My problem ended up being because the spreadsheet that EPPlus was reading was an .xlsx spreadsheet created by LibreOffice Calc, not by Microsoft Excel. Even though LibreOffice was exporting in the right format, and the spreadsheet could be opened in Excel, something was missing that EPPlus was expecting. As soon as I saved the spreadsheet from Excel instead of LibreOffice, no issues.
I got the same issue with 4.5.3.2 version.
Just overcome by using the second call time.

Excel workbook import errors

I'm working on a Jeopardy application for some practice, and I'm having trouble understanding why I am getting this error. I've narrowed it down to what I believe the issue is, but for now, here's my code...
if (questionPath != "")
{
Excel.Application myapp = new Excel.Application();
Excel.Workbook wb = myapp.Workbooks.Open(questionPath);
Excel.Worksheet sheet = (Excel.Worksheet)wb.Worksheets.get_Item(1);
var cell = (Excel.Range)sheet.Cells[1, 1];
MessageBox.Show(cell.Value);
}
Basically, sets up a new Excel, pulls in the workbook, gets the first sheet, and then looks at whatever cell I tell it to look at and displays the value (like I said, just testing/playing around).
However, if the value in cell A1 is an integer, I get a "RuntimeBinderException was unhandled" error. It says that
"An unhandled exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll
Additional information: The best overloaded method match for 'System.Windows.Forms.MessageBox.Show(string)' has some invalid arguments"
However if the value is a STRING, then it works fine. I've tried using Cell.ToString(), which obviously just gives the cell as a COM. I've tried using Cell.Value.ToString, which doesn't exist.... Looking at the intellisense it says that the value IS actually being read in (I used '11' as the value).
Also, I noticed that if I ran my code below, and then tried to open up the Excel to edit it, it tells me that the Excel is locked for editting by myself. I'm guessing I need to close the connection string, but I can't figure out where to do that.
Would I close myapp, or wb, or sheet? I tried a variety of _.Close()'s and other things I thought seemed obvious, but nada. Any tips on this as well?
Thanks everyone!
For anyone else reading this, 6 years later ...
I would recommend against using ToSting() if there is a possibility of null data.
Convert.ToString() handles null, while ToString() doesn't.
In the above scenario, this:
var temp = cell.Value.ToString();
Would be much safer being this:
var temp = Convert.ToString(cell.Value);
That way, it can deal with any null data, unlike ToString().
So I found out the reason it was doing that... it's pulling in an integer. Duh. I guess you can't use Cell.Value.ToString(); in the MessageBox.Show event, so here's what I ended up needing to do, and it worked perfectly.
if (questionPath != "")
{
Excel.Application myapp = new Excel.Application();
Excel.Workbook wb = myapp.Workbooks.Open(questionPath);
Excel.Worksheet sheet = (Excel.Worksheet)wb.Worksheets.get_Item(1);
var cell = (Excel.Range)sheet.Cells[1, 1];
var temp = cell.Value.ToString();
MessageBox.Show(temp);
}
Thanks for your help! I don't seem to be getting any issues with the Excel being locked to read-only, but I didn't change anything for that either... Assuming it was user error

How to paste CSV data to Windows Clipboard with C#

What I'm trying to accomplish
My app generates some tabular data
I want the user to be able to launch Excel and click "paste" to place the data as cells in Excel
Windows accepts a format called "CommaSeparatedValue" that is used with it's APIs so this seems possible
Putting raw text on the clipboard works, but trying to use this format does not
NOTE: I can correctly retrieve CSV data from the clipboard, my problem is about pasting CSV data to the clipboard.
What I have tried that isn't working
Clipboard.SetText()
System.Windows.Forms.Clipboard.SetText(
"1,2,3,4\n5,6,7,8",
System.Windows.Forms.TextDataFormat.CommaSeparatedValue
);
Clipboard.SetData()
System.Windows.Forms.Clipboard.SetData(
System.Windows.Forms.DataFormats.CommaSeparatedValue,
"1,2,3,4\n5,6,7,8",
);
In both cases something is placed on the clipboard, but when pasted into Excel it shows up as one cell of garbarge text: "–§žý;pC¦yVk²ˆû"
Update 1: Workaround using SetText()
As BFree's answer shows SetText with TextDataFormat serves as a workaround
System.Windows.Forms.Clipboard.SetText(
"1\t2\t3\t4\n5\t6\t7\t8",
System.Windows.Forms.TextDataFormat.Text
);
I have tried this and confirm that now pasting into Excel and Word works correctly. In each case it pastes as a table with cells instead of plaintext.
Still curious why CommaSeparatedValue is not working.
The .NET Framework places DataFormats.CommaSeparatedValue on the clipboard as Unicode text. But as mentioned at http://www.syncfusion.com/faq/windowsforms/faq_c98c.aspx#q899q, Excel expects CSV data to be a UTF-8 memory stream (it is difficult to say whether .NET or Excel is at fault for the incompatibility).
The solution I've come up with in my own application is to place two versions of the tabular data on the clipboard simultaneously as tab-delimited text and as a CSV memory stream. This allows the destination application to acquire the data in its preferred format. Notepad and Excel prefer the tab-delimited text, but you can force Excel to grab the CSV data via the Paste Special... command for testing purposes.
Here is some example code (note that WinForms-equivalents from the WPF namespaces are used here):
// Generate both tab-delimited and CSV strings.
string tabbedText = //...
string csvText = //...
// Create the container object that will hold both versions of the data.
var dataObject = new System.Windows.DataObject();
// Add tab-delimited text to the container object as is.
dataObject.SetText(tabbedText);
// Convert the CSV text to a UTF-8 byte stream before adding it to the container object.
var bytes = System.Text.Encoding.UTF8.GetBytes(csvText);
var stream = new System.IO.MemoryStream(bytes);
dataObject.SetData(System.Windows.DataFormats.CommaSeparatedValue, stream);
// Copy the container object to the clipboard.
System.Windows.Clipboard.SetDataObject(dataObject, true);
Use tabs instead of commas. ie:
Clipboard.SetText("1\t2\t3\t4\t3\t2\t3\t4", TextDataFormat.Text);
Just tested this myself, and it worked for me.
I have had success pasting into Excel using \t (see BFree's answer) as column separators and \n as row separators.
I got the most success defeating formatting issues by using a CSV library (KBCsv) to write the data into a CSV file in the temp folder then open it in Excel with Process.Start(). Once it is in Excel the formatting bit is easy(er), copy-paste from there.
string filePath = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString() + ".csv";
using (var streamWriter = new StreamWriter(filePath))
using (CsvWriter csvWriter = new CsvWriter(streamWriter))
{
// optional header
csvWriter.WriteRecord(new List<string>(){"Heading1", "Heading2", "YouGetTheIdea" });
csvWriter.ValueSeparator = ',';
foreach (var thing in YourListOfThings ?? new List<OfThings>())
{
if (thing != null)
{
List<string> csvLine = new List<string>
{
thing.Property1, thing.Property2, thing.YouGetTheIdea
};
csvWriter.WriteRecord(csvLine);
}
}
}
Process.Start(filePath);
BYO Error handing & logging.

Categories

Resources