EPPlus import file xlsm - c#

I am trying to import an xlsm file using EPPlus. I found the link that said EPPlus doesn't support this : Convert XLSM to XLSX . It's very old so I wanted to know if it supports it now ? If not, is there any way to do this with EPPlus by converting to XLSX but the way should be independent on Microsoft updates and just dependent on libraries used at the time of building the program.
My current code looks like this :
FileInfo file = new FileInfo(Path.Combine(Program.source_path, s));
using (ExcelPackage package = new ExcelPackage(file))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[3];
for (int row = 9; row <= worksheet.Dimension.End.Row; row++)
{
}
}

This will copy all content from the source xlsm file to a target xlsx file.
using (var target = new ExcelPackage(new System.IO.FileInfo("D:\\target.xlsx")))
{
using (var source = new ExcelPackage(new System.IO.FileInfo("D:\\source.xlsm")))
{
foreach (var worksheet in source.Workbook.Worksheets)
{
target.Workbook.Worksheets.Add(worksheet.Name, worksheet);
}
}
target.Save();
}

Related

OpenXML - embedding objects in Excel C#

I am trying to embed object into .xlsx document and copy sheets with embedded objects.
1. Copying sheets
This looks like straight forward issue. I have created method to copy the sheets:
static void CopySheetInsideWorkbook(string filename, string sheetName, string clonedSheetName)
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(filename, true))
{
WorkbookPart workbookPart = spreadsheetDocument.WorkbookPart;
WorksheetPart sourceSheetPart = GetWorksheetPartByName(spreadsheetDocument, sheetName);
SpreadsheetDocument tempSheet =
SpreadsheetDocument.Create(new MemoryStream(), spreadsheetDocument.DocumentType);
WorkbookPart tempWorkbookPart = tempSheet.AddWorkbookPart();
WorksheetPart tempWorksheetPart = tempWorkbookPart.AddPart<WorksheetPart>(sourceSheetPart);
WorksheetPart clonedSheet = workbookPart.AddPart<WorksheetPart>(tempWorksheetPart);
Sheets sheets = workbookPart.Workbook.GetFirstChild<Sheets>();
Sheet copiedSheet = new Sheet
{
Name = clonedSheetName,
Id = workbookPart.GetIdOfPart(clonedSheet),
SheetId = (uint) sheets.ChildElements.Count + 1
};
sheets.Append(copiedSheet);
workbookPart.Workbook.Save();
}
}
The ouput is as expected but the embedded files are copied as "Picture" rather than "Object". I unzipped .xlsx file and all looks legit ie. similar to the sheet I copied. Yet still the file cannot be opened on the copied sheet. All images, strings are displayed in correct way.
2. Embedding the object
What I understand I need to do is:
Convert object into oleObject - this will be separate fun.
Add DrawingsPart - It looks like it's read-only and I can only add ImagePart.
Embed Object
Connect both drawing and embedded object part toghether and allocate to some range in spreadsheet.
static void EmbedFileXlsx(string path, string embeddedFilePath, string placeholderImagePath)
{
using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(path, true))
{
WorksheetPart sourceSheetPart = GetWorksheetPartByName(spreadsheetDocument, "Test");
var imagePart = sourceSheetPart.AddImagePart(ImagePartType.Emf, "rId1");
imagePart.FeedData(File.Open(placeholderImagePath, FileMode.Open));
var embeddedObject =
sourceSheetPart.AddEmbeddedObjectPart(#"application/vnd.openxmlformats-officedocument.oleObject");
embeddedObject.FeedData(File.Open(embeddedFilePath, FileMode.Open));
spreadsheetDocument.Save();
}
}
This code just adds embedded objects into the file but does not create any type of relationship between them. This means that file is not visible on the spreadsheet.
I tried copying sheets using ClosedXML as well but unfortunately this is not supported nor the embedding.
I also managed to understand how I can copy sheet into new document with all embedded objects using .xml files inside spreadsheet but I do not think this would be much productive and I would like to achieve this using all the methods inside OpenXML. It looks everything is there but something is amiss.
I am no expert in this, but could this help you on your way?
spreadsheetDocument.CreateRelationshipToPart(SOME ID);

Asp.net Core Import Excel File FileNotFoundException

I've had this working but it has stopped and I can't figure out why. I'm importing a simple excel file, using EPPlus.Core v1.3. Here's the code I'm using:
public async Task<IActionResult> Import(IFormFile file)
{
//Get file
var newfile = new FileInfo(file.FileName);
var fileExtension = newfile.Extension;
//Check if file is an Excel File
if (fileExtension.Contains(".xls"))
{
//Create an excel package
using (var package = new ExcelPackage(newfile))
{
//Get the first worksheet in the file
var worksheet = package.Workbook.Worksheets[1];
...
The var worksheet line throws an error
"IndexOutOfRangeException: Worksheet position out of range."
However, when I look at the package variable I see this error
"Length = 'package.File.Length' threw an exception of type
'System.IO.FileNotFoundException'"
What am I missing here? Like I said, this used to work and I can't think of anything I changed related to this code to cause this issue.
Is it the same file also? It looks like the file might not have two worksheets and since the index is 1, it is looking for the second worksheet.
You need to load the file in a MemoryStream and create a ExcelPackage from there.
using (MemoryStream ms = new MemoryStream(FileUpload1.FileBytes))
using (ExcelPackage excelPackage = new ExcelPackage(ms))
{
//work with the excel document
}
FileUpload1.FileBytes if for asp.net webforms, but I'm guessing Core has something similar.

.xlsx Created and Saved Using Template with EPPlus is Unreadable/Corrupt

The Problem
I'm trying to create a small program that uses an Excel template to create an Excel document and then writes to several cells using EPPlus. Unfortunately, the files appear to be corrupt no matter what I try.
My code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OfficeOpenXml;
using System.IO;
namespace ConsoleApplication9
{
public sealed class ExcelSerialize
{
private readonly List<Tuple<string, string>> Results;
private readonly string Directory;
private ExcelPackage package;
public ExcelSerialize(List<Tuple<string, string>> Results, string Directory)
{
this.Results = Results;
this.Directory = Directory;
}
public bool WriteResults()
{
FileInfo template = new FileInfo(Directory);
using (package = new ExcelPackage(template, true))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
//foreach (Tuple<string, string> Result in Results)
//{
// worksheet.Cells[Result.Item1].Value = Result.Item2;
//}
string file = string.Format(System.AppDomain.CurrentDomain.BaseDirectory.ToString() + #"results\results" + System.DateTime.Now.ToString().Replace(" ", "").Replace("/", "_").Replace(":", "-") + ".xlsx");
Byte[] bin = package.GetAsByteArray();
File.WriteAllBytes(file, bin);
return true;
}
}
}
}
Things I've tried:
Changing the values of various cells in the template.
Saving an Excel document created from the template without writing any new data to it.
Creating a basic template with the cells A1, A2, and A3 containing "TEST" and no other edits instead of the more complicated template I intend to use.
Saving using Package.SaveAs().
Saving using the Byte array seen in the example code.
Compiling EPPlus from the latest source provided on Codeplex.
Things that work:
Creating a new file using the following code:
using (package = new ExcelPackage(string.Format(System.AppDomain.CurrentDomain.BaseDirectory.ToString() + #"results\results" + System.DateTime.Now.ToString().Replace(" ", "").Replace("/", "_").Replace(":", "-") + ".xlsx"))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("Test");
worksheet.Cells[A1].Value = "Test";
package.Save();
}
Notes:
For whatever reason, the files saved still appear corrupt and can't be recovered. I'm currently using Microsoft Office 2010. The file formats I'm using are .xltx and .xlsx.
Short Answer:
The EPPlus library does not support creation of a file from an existing xltx Excel template.
Use a pre-formatted xlsx file instead.
Long Answer:
Dim excelFile As New FileInfo(filename)
Dim reportTemplate As New FileInfo(templatePath)
Dim xlFile As ExcelPackage = New ExcelPackage(excelFile, reportTemplate)
' Do Stuff
xlFile.Save()
When instantiating a new ExcelPackage object using EPPlus you're supposed to be able to provide a template as the second parameter (see code snippet above), however when providing it with an xltx file I kept getting a corrupt output file same as the user above. I eventually found that supplying a regular xlsx file as the template, rather than an actual template file, appeared to work.
This is an open source package, so I decided to take a quick look at the source.
It looks like the ExcelPackage constructors that accept the "template" parameter, call the CreateFromTemplate() method. The comments on the CreateFromTemplate() method states that it expects an existing xlsx file to use as a template, not an actual template file.
So while one might assume that the 'template' parameter refers to an actual xltx template file, it appears that EPPlus does not support that.
Similar problem faced, this worked by just giving me back the template
public ExcelPackage getSheet(string templatePath){
FileInfo template = new FileInfo(templatePath);
ExcelPackage p = new ExcelPackage(template, true);
ExcelWorksheet ws = p.Workbook.Worksheets[1]; //position of the worksheet
ws.Name = bookName;
p.Save();
ExcelPackage pck = new ExcelPackage(new System.IO.MemoryStream(), p.Stream);
return pck;
Then you call with this
string path = Server.MapPath(#"../../../Ex/template.xlsx")
try
{
OfficeOpenXml.ExcelPackage pck = getSheet(path);
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", String.Format(System.Globalization.CultureInfo.InvariantCulture, "attachment; filename={0}", fileName + ".xlsx"));
Response.BinaryWrite(pck.GetAsByteArray());
Response.End();
}
catch { }
Explicitly close the File after you write the data to it.
I know when you use EPPlus in a response on a web request closing the response removes that issue.

Find and replace value in Excel using C#

How can I find some value from cell and replace by new value in Excel?
I tryed this but it doesn't works:
Microsoft.Office.Interop.Excel.Application xlapp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook wb =default(Microsoft.Office.Interop.Excel.Workbook);
wb = xlapp.Workbooks.Open(FileName.ToString());
wb.Worksheets[0].Cells.Replace("find","replace");
I would recommend you use NPOI which can be accessed either via codeplex or directly through Nuget in Visual Studio. It gives you the ability to easily upload, edit and create spreadsheets in .NET
Example of uploading a spreadsheet:
HSSFWorkbook hssfworkbook;
void InitializeWorkbook(string path)
{
//read the template via FileStream, it is suggested to use FileAccess.Read to prevent file lock.
//book1.xls is an Excel-2007-generated file, so some new unknown BIFF records are added.
using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read))
{
hssfworkbook = new HSSFWorkbook(file);
}
}
You can then use the IRow and ICell collections of the spreadsheet to locate and edit the data you need before doing an export.
More examples can be found here
If interested, you can use GemBox.Spreadsheet for this, like so:
SpreadsheetInfo.SetLicense("FREE-LIMITED-KEY");
// Load your XLS, XLSX, ODS or CSV file.
ExcelFile wb = ExcelFile.Load(FileName.ToString());
ExcelWorksheet ws = wb.Worksheets[0];
// Replace all "find" occurances with "replace" text.
int row, column;
while(ws.Cells.FindText("find", out row, out column))
ws.Cells[row, column].ReplaceText("find", "replace");
// Save your XLS, XLSX, ODS or CSV file.
wb.Save(FileName.ToString());
Also you can find another searching in Excel example here.
All you have to do is replace
wb.Worksheets[0].Cells.Replace("find","replace");
with
wb.Worksheets[1].Cells.Replace("find","replace");

Convert xls or xlsx file with multiple sheets into one csv file using interop

I am trying to convert a xls or xlsx file with multiple sheets into one CSV file using c# and the interop library. I am only getting the one sheet in the CSV file. I know I can specify the sheet to save as or change the active sheet to save that one but I am looking for a solution to append all the sheets to the same CSV file that will work with both xls and xlsx files. I am automating this and don't care what is in the excel document just want to pull the string values out and append it to the csv file. Here is the code I am using:
Microsoft.Office.Interop.Excel.Application app = new Microsoft.Office.Interop.Excel.Application();
app.Visible = false;
app.DisplayAlerts = false;
Workbook wkb = app.Workbooks.Open(fullFilePath);
wkb.SaveAs(newFileName, XlFileFormat.xlCSVWindows);
Is this even possible?
I'm just getting started tackling a similar situation, but I believe this may address your needs:
http://www.codeproject.com/Articles/246772/Convert-xlsx-xls-to-csv
This uses the ExcelDataReader api that you can get from NuGet
http://exceldatareader.codeplex.com/
Like Tim was saying, you're going to have to make sure and possibly validate that the columns and structure are the same between sheets. You may also have to eat the header rows on all the sheets after the first one. I'll post an update and some code samples once I've finished.
Update [7/15/2013]. Here's my finished code. Not very fancy, but it gets the job done. All of the sheets are tables in the DataSet, so you just loop through the tables adding onto your destination. I'm outputting to a MongoDB, but I'm guessing you can swap that out for a StreamWriter for your CSV file rather easily.
private static void ImportValueSetAttributeFile(string filePath)
{
FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read);
// Reading from a OpenXml Excel file (2007 format; *.xlsx)
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
// DataSet - The result of each spreadsheet will be created in the result.Tables
DataSet result = excelReader.AsDataSet();
// Free resources (IExcelDataReader is IDisposable)
excelReader.Close();
var connectionString = ConfigurationManager.ConnectionStrings[0].ConnectionString;
var database = ConfigurationManager.AppSettings["database"];
var mongoAccess = new MongoDataAccess(connectionString, database);
var cdm = new BaseDataManager();
int ind = 0;
for (int i = 0; i < result.Tables.Count; i++)
{
int row_no = 1;
while (row_no < result.Tables[ind].Rows.Count) // ind is the index of table
// (sheet name) which you want to convert to csv
{
var currRow = result.Tables[ind].Rows[row_no];
var valueSetAttribute = new ValueSetAttribute()
{
CmsId = currRow[0].ToString(),
NqfNumber = currRow[1].ToString(),
ValueSetName = currRow[2].ToString(),
ValueSetOid = currRow[3].ToString(),
Definition = currRow[4].ToString(),
QdmCategory = currRow[5].ToString(),
Expansion = currRow[6].ToString(),
Code = currRow[7].ToString(),
Description = currRow[8].ToString(),
CodeSystem = currRow[9].ToString(),
CodeSystemOid = currRow[10].ToString(),
CodeSystemVersion = currRow[11].ToString()
};
cdm.AddRecords<ValueSetAttribute>(valueSetAttribute, "ValueSetAttributes");
row_no++;
}
ind++;
}
}

Categories