Has anybody an idea what may cause this error
'Wrong Local header signature: 0xE011CFD0'
var path = #"C:\Excel.xls";
using (var fs = File.OpenRead(path))
{
var wb = new XSSFWorkbook(fs);
}
Im using: https://github.com/dotnetcore/NPOI
Leaving this in case other people pass by here.
.xls is the old Excel format. With that format you should create a new HSSFWorkbook instance. The XSSFWorkbook is used with the new .xlsx format.
Both types implement the IWorkbook interface so you can build your code around this interface and determine the workbook instance's type at runtime.
I created a simple Excel component and here is the constructor:
private readonly bool _useOldExcelFormat;
private readonly IWorkbook _workbook;
public NpoiExcelManager(bool useOldExcelFormat = false)
{
_useOldExcelFormat= useOldExcelFormat;
if (_useOldExcelFormat)
{
_workbook = new HSSFWorkbook();
}
else
{
_workbook = new XSSFWorkbook();
}
}
You'll discover a few differences between HSSFWorkbook and XSSFWorkbook so sometimes you'd have to write specific code for each implementation but those cases are very rare.
I'd say around 95% of the IWorkbook interface is working for both implementations.
Ok, I found solution:
Saved that Excel file as an .xlsx but without macros or .xlsm and it worked
Related
My customer has a use case for exporting search results to a spreadsheet. I would like to return a formatted spreadsheet to them, but the only way I can get the formatting changes to "stick" is by calling
workbook.Save(memoryStream, SaveFormat.Xlsx);
The problem with calling the method above, is that a spreadsheet will actually be saved to my local project folder, which is not desired behavior. How can I return the spreadsheet without calling workbook.Save()?
public byte[] ExportSpreadsheet(List<Result> results)
{
var workbook = MakeWorkbook(results);
var memoryStream = new MemoryStream();
workbook.Save(memoryStream, SaveFormat.Xlsx); // this saves the spreadsheet in the project
memoryStream.Seek(0, SeekOrigin.Begin);
var byteArray = memoryStream.ToArray();
return byteArray;
}
private Workbook MakeWorkbook(List<Result> results)
{
var workbook = new Workbook();
AddDataToWorkbook(workbook);
ApplyFormattingAfterData(workbook);
return workbook;
}
workbook.Save(memoryStream, SaveFormat.Xlsx);
You are doing ok. This line will save the workbook to stream and not on physical filepath. It won't save to your project's folder or path.
PS. I am working as Support developer/ Evangelist at Aspose.
I am trying to read a digital signature in the excel file
I need to read Signature Text(The person's name) and Signature Title(His designation/Title under signature line) .I can do it via Interop.Excel and openOffice.xml , but i still need to do the same thing via EPPlus. Is it possible to do the same thing via EPPlus. Please find the code for Interop.Excel
Excel.Workbook excelWorkbook = excelApp.Workbooks.Open(strFile)
SignatureSet allSignatures = excelWorkbook.Signatures;
foreach (Signature digitalSign in allSignatures)
{
signedTitle = digitalSign.Setup.SuggestedSignerLine2;
signedName = digitalSign.Details.SignatureText;
}
Is this what you need:
using (var xls = new ExcelPackage(fileInfo))
{
var name = xls.Workbook.Properties.Author;
var title = xls.Workbook.Properties.Title;
}
I don't see any other signatures (other than Zip file-related) in EPPlus:
https://github.com/JanKallman/EPPlus/search?q=signature&unscoped_q=signature
When using Poi, even it doesn't have the sheet, it automatically creates the sheet. How to know whether it contains the sheet?
public bool Check(string Filepath, string sheetname)
{
HSSFWorkbook workbook;
using(FileStream stream = new FileStream(Filepath, FileMode.Open, FileAccess.Read){
workbook = new HSSFWorkbook(stream);
}
return workbook.contains(sheetname);
Unfortunately, NPOI / POI does not really have that feature. There is a worksheet.GetSheetAt(index) method, but it throws an exception when you try to get an index that does not exist. So if you want to try looping through possible index numbers, make sure you put a try-catch inside your loop.
I can successfully inject a piece of VBA code into a generated excel workbook, but what I am trying to do is use the Workbook_Open() event so the VBA code executes when the file opens. I am adding the sub to the "ThisWorkbook" object in my xlsm template file. I then use the openxml productivity tool to reflect the code and get the encoded VBA data.
When the file is generated and I view the VBA, I see "ThisWorkbook" and "ThisWorkbook1" objects. My VBA is in "ThisWorkbook" object but the code never executes on open. If I move my VBA code to "ThisWorkbook1" and re-open the file, it works fine. Why is an extra "ThisWorkbook" created? Is it not possible to inject an excel spreadsheet with a Workbook_Open() sub? Here is a snippet of the C# code I am using:
private string partData = "..."; //base 64 encoded data from reflection code
//open workbook, myWorkbook
VbaProjectPart newPart = myWorkbook.WorkbookPart.AddNewPart<VbaProjectPart>("rId1");
System.IO.Stream data = GetBinaryDataStream(partData);
newPart.FeedData(data);
data.Close();
//save and close workbook
Anyone have ideas?
Based on my research there isn't a way to insert the project part data in a format that you can manipulate in C#. In the OpenXML format, the VBA project is still stored in a binary format. However, copying the VbaProjectPart from one Excel document into another should work. As a result, you'd have to determine what you wanted the project part to say in advance.
If you are OK with this, then you can add the following code to a template Excel file in the 'ThisWorkbook' Microsoft Excel Object, along with the appropriate Macro code:
Private Sub Workbook_Open()
Run "Module1.SomeMacroName()"
End Sub
To copy the VbaProjectPart object from one file to the other, you would use code like this:
public static void InsertVbaPart()
{
using(SpreadsheetDocument ssDoc = SpreadsheetDocument.Open("file1.xlsm", false))
{
WorkbookPart wbPart = ssDoc.WorkbookPart;
MemoryStream ms;
CopyStream(ssDoc.WorkbookPart.VbaProjectPart.GetStream(), ms);
using(SpreadsheetDocument ssDoc2 = SpreadsheetDocument.Open("file2.xlsm", true))
{
Stream stream = ssDoc2.WorkbookPart.VbaProjectPart.GetStream();
ms.WriteTo(stream);
}
}
}
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[short.MaxValue + 1];
while (true)
{
int read = input.Read(buffer, 0, buffer.Length);
if (read <= 0)
return;
output.Write(buffer, 0, read);
}
}
Hope that helps.
I found that the other answers still resulted in the duplicate "Worksheet" object. I used a similar solution to what #ZlotaMoneta said, but with a different syntax found here:
List<VbaProjectPart> newParts = new List<VbaProjectPart>();
using (var originalDocument = SpreadsheetDocument.Open("file1.xlsm"), false))
{
newParts = originalDocument.WorkbookPart.GetPartsOfType<VbaProjectPart>().ToList();
using (var document = SpreadsheetDocument.Open("file2.xlsm", true))
{
document.WorkbookPart.DeleteParts(document.WorkbookPart.GetPartsOfType<VbaProjectPart>());
foreach (var part in newParts)
{
VbaProjectPart vbaProjectPart = document.WorkbookPart.AddNewPart<VbaProjectPart>();
using (Stream data = part.GetStream())
{
vbaProjectPart.FeedData(data);
}
}
//Note this prevents the duplicate worksheet issue
spreadsheetDocument.WorkbookPart.Workbook.WorkbookProperties.CodeName = "ThisWorkbook";
}
}
You need to specify "codeName" attribute in the "xl/workbook..xml" object
After feeding the VbaProjectPart with macro. Add this code:
var workbookPr = spreadsheetDocument.WorkbookPart.Workbook.Descendants<WorkbookProperties>().FirstOrDefault();
workbookPr.CodeName = "ThisWorkBook";
After opening the file everything should work now.
So, to add macro you need to:
Change document type to macro enabled
Add VbaProjectPart and feed it with earlier created macro
Add workbookPr codeName attr in xl/workbook..xml with value "ThisWorkBook"
Save as with .xlsm ext.
Our application has distribution functionality. It takes several Excel 2007 spreadsheets, copies them into a single sheet, then emails them to the users. The problem is that images and charts are not copying over. I have tried everything I can find on here and various other sources, but sadly nothing seems to work. Our application is written using VSTO, and I have also tried OpenXML, but nothing seems to work. In fact trying to copy over in OpenXML corrupted the files so badly that Excel could not read or recover them. Here's the code we use (note: we have ExcelWrapper simply calls the same functions on the Excel App but hides all the optional items).
private void CreateWorkbook(string filePath, string batchReportsPath)
{
//place the xlsx file into a workbook.
//call getfilesnames
Excel.Workbook bookToCopy;
Excel.Workbook newWorkbook;
Excel.Worksheet tempSheet = new Excel.Worksheet();
newWorkbook = ExcelWrapper.WorkbooksAdd(ExcelApp.Workbooks);
if (File.Exists(filePath))
File.Delete(filePath);
ExcelWrapper.WorkbookSaveAs(newWorkbook, filePath);
List<string> filePaths = new List<string>(Directory.GetFiles(batchReportsPath));
filePaths.ForEach(delegate(string reportPath)
{
string reportPathAndName = reportPath;
bookToCopy = ExcelWrapper.WorkbooksOpen(ExcelApp.Workbooks, reportPathAndName);
int nextSheetNumber = newWorkbook.Sheets.Count;
((Excel.Worksheet)sheetToSend.Sheets[1]).Copy(Type.Missing, newWorkbook.Sheets[nextSheetNumber]);
ExcelWrapper.WorkbookClose(bookToCopy);
});
newWorkbook.Save();
ExcelWrapper.WorkbookClose(newWorkbook);
bookToCopy= null;
tempSheet = null;
newWorkbook = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
}
I have tried every promising option and searched both the VSTO and OpenXML object models and I am stumped. Please stackoverflow community, you're my only hope.
UPDATE: Here's the answer folks:
//Copy all the images, Charts, shapes
foreach (Excel.Shape o in copySheet.Shapes)
{
if (o.Type == Microsoft.Office.Core.MsoShapeType.msoPicture)
o.CopyPicture();
else
o.Copy();
newWorkbook.Save();
}
You need to do the save after each copy to get the Paste to finalize. Thanks for your input.
You'll need to check the WORKSHEET object of the worksheet you're looking to copy, then run through all the "*Objects" properties, and, for each of those collections, write code to manually copy all the elements in that collection to the new sheet.
For example, you've got:
ChartObjects
ListObjects
OleObjects
Shapes (Which might get copied along with the sheet, I'm not sure).
Perhaps you can get there by a copy/paste? Here's an example that copies cell data, charts and images: MSDN
Good luck!
I don't have an answer but I can give you some pointers. First, this is not an openxml question, it's a vsto question. Second, images and charts are attached to a spreadsheet, they are not content in it (a subtle distinction). There is probably a property of all charts and one of all images attached to the sheet. Copy that across.
(I use the IExtensibility interface instead of VSTO so I know what's in the underlying API, but not the property names - sorry.)
This is how I do it -- moves the entire worksheet from source workbook to a brand new workbook:
public static void MoveWorksheet()
{
public static Excel.Application oXL;
public static Excel._Worksheet ws;
if (File.Exists(Global.Variables.GlobalVariables.recap))
{
//workbookToOpen is location and name
oXL.Workbooks.Open(workbookToOpen, Missing.Value, true);
object ws = null;
try
{
ws = wb.Sheets["Sheet 1"];
if (ws != null)
{
((Worksheet)ws).UsedRange.Interior.ColorIndex = Constants.xlNone;
((Worksheet)ws).Copy();
oXL.ActiveWorkbook.SaveAs("SaveAsFileName" + ".xlsx");
oXL.ActiveWorkbook.Close(true);
}
}
catch {}
}
}