I use Spreadsheetgear to export the results of custom SQL queries as excel files.
Now I want to improve this system: The user will be able to upload an excel template file into the database (currently as varbinary). For example, it could have one worksheet with calculations, then when exporting data into that template it'll fill a different worksheet with the datatable from the query.
Can spreadsheetgear do this? If so, how does it work - mainly how can I load an existing excel file as a Spreadsheetgear workbook/workbookset? I could not find anything in their documentation (though I am still looking).
Edit: Solved.
I create the workbook manually, load the template from the database as a byte[], then open said template with the OpenFromMemory function:
// Create workbookSet
SpreadsheetGear.IWorkbookSet workbookSet = SpreadsheetGear.Factory.GetWorkbookSet();
// Create a new empty workbook in the workbookSet.
SpreadsheetGear.IWorkbook workbook = workbookSet.Workbooks.Add();
if(TemplateID != -1) // If this case requires a template
{
// Get template from SQL database (.xlsx stored as varbinary(max))
byte[] template = GetTemplateByID(VorlagenID);
workbook = workbookSet.Workbooks.OpenFromMemory(template);
}
// Create export worksheet
SpreadsheetGear.IWorksheet worksheet = workbook.Worksheets[0];
worksheet.Name = "Export";
[...]
Templates always use the Worksheet[1] in my case, but it should be easy to create a Worksheet[1] for the export.
Yes it is possible to set up pre-constructed template files using SpreadsheetGear. We use this extensively using .NET / C# / MSSSQL. The method allows you to create quite sophisticated templates and then simply add the required data. This of course includes any calculations you build into the template.
Method 1 - Store the template on a webserver, extract and write the created user spreadsheet to a folder on the web server. Return the filename to allow extraction by code or by the user from the server.
public static String SaveTemplateSpreadsheetToServer()
{
// Open the workbook.
var templatename = HostingEnvironment.MapPath("~/Files/MyTemplate.xlsx");
var workbook = Factory.GetWorkbook(templatename );
// Read and write to the spreadsheet
// Save a copy to disk and return filename
var filename = "The_exported_file.xlsx";
var filePath = HostingEnvironment.MapPath("~/FilesTemp/" + filename);
workbook.SaveAs(filePath, FileFormat.OpenXMLWorkbook);
// close workbook
workbook.Close();
// Return the filename
return fileName;
}
Method 2: Store the template on a webserver, extract and save modified spreadsheet as a byte array. Download directly an attachment
public static byte[] SaveTemplateSpreadsheetToServer()
{
// Open the workbook.
var templatename = HostingEnvironment.MapPath("~/Files/MyTemplate.xlsx");
var workbook = Factory.GetWorkbook(templatename );
// Read and write to the spreadsheet
// Save as byte array and send to user
var byteArray = workbook.SaveToMemory(FileFormat.OpenXMLWorkbook);
// close workbook
workbook.Close();
// Return the byte array
return byteArray;
}
We have done some work with binary template files saved in a database but find it more convenient to work with physical template files on a web server. It is easier to manage changes to the template.
My only caution is to avoid working with very big templates that have lots of "junk" in them (e.g. images). The process becomes affected by the time it takes to load the file into memory prior to the read / write / export activity. Less than 1MB is ideal and less than 2MB is manageable.
Related
I have a project where my goal is to produce an .xlsm Excel spreadsheet using .NET and the EEPlus 5.8.14 Excel Spreadsheet library. I can do this using EEPlus's documented techniques, (though some of these I cannot get to work). As I was working on this, I realized that what my code needed to do was relatively small, and it made sense to use an existing .xlsm file as a template and just make changes to what I needed to change using EEPlus.
So now I am including the .xlsm file as a resource compiled into the assembly. This works great, and I can read the file from the resources and produce it from my controller. But once read, this data inside EPPlus seems to be read-only. So while this produces an Excel file:
public ActionResult ExcelFile(){
const string ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Byte[] bytes = Properties.Resources.AssetsEntry;
string fstem = Path.GetRandomFileName();
int unique = 0;
string filePath = String.Format("{0}#AutoGen_{1}_{2}.{3}", Path.GetTempPath(), fstem, ++unique, "xlsm");
var outStream = System.IO.File.OpenWrite(filePath);
var writer = new BinaryWriter(outStream);
writer.Write(bytes);
outStream.Close();
ExcelPackage excelPackage = new ExcelPackage(filePath);
var sheet = excelPackage.Workbook.Worksheets[1];
//place where I might want to change data
//sheet.Cells["B3"].Value = "testing";
var excelData = excelPackage.GetAsByteArray();
var fileName = "ExcelFile.xlsm";
return File(excelData, ContentType, fileName);
}
If I try to uncomment out the second commented-out line, that code fails to change the resulting Excel spreadsheet (though there is no error). How do I go about reading in an Excel spreadsheet and making changes using EEPlus?
UPDATE: I can add new worksheets to an uploaded spreadsheet, and I can alter those added sheets. But I cannot alter data on uploaded worksheets. Fortunately, for this particular project, that is acceptable. But it would be frustrating if I wanted to be able to set up a worksheet in Excel and then populate it programmatically.
I have a VSTO addin that receives an excel chart (in byte array) over a network from a server. I would like to paste this chart onto a powerpoint slide programmatically without having to save the chart to disk first.
However, when I look through all the .Add* methods exposed by Microsoft.Office.Interop.PowerPoint.Shapes, they all seem to require a file path in string . In other words, I would have to convert the binary data to a file in a supported format on the system and get its path before I can use those functions.
Are there any way to directly use this binary data and paste it on the powerpoint slide without having to save it as a file on the system first?
using Microsoft.Office.Interop.Excel;
void CreateChart()
{
ChartData gChartData;
Workbook gWorkBook;
Worksheet gWorkSheet;
// Create the chart and set a reference to the chart data.
var myChart = ActivePresentation.Slides[1].Shapes.AddChart() as Microsoft.Office.Interop.PowerPoint.Chart;
gChartData = myChart.ChartData;
// Set the Workbook and Worksheet references.
gWorkBook = gChartData.Workbook;
gWorkSheet = gWorkBook.Worksheets[1];
// Add the data to the workbook.
gWorkSheet.ListObjects["Table1"].Resize(gWorkSheet.Range["A1:B5"]);
gWorkSheet.Range["Table1[[#Headers],[Series 1]]"].Value = "Items";
gWorkSheet.Range["a2"].Value = "Coffee";
gWorkSheet.Range["a3"].Value = "Soda";
gWorkSheet.Range["a4"].Value = "Tea";
gWorkSheet.Range["a5"].Value = "Water";
gWorkSheet.Range["b2"].Value = "1000";
gWorkSheet.Range["b3"].Value = "2500";
gWorkSheet.Range["b4"].Value = "4000";
gWorkSheet.Range["b5"].Value = "3000";
//ToDo: Style
}
fill the data as you like (you may fill it with a for loop and fill the $"a{i}" column with the number of the entry (i))
See this Article for further Information
UPDATE 21.04.2021:
This isn't what the OP asked for.
In his comment he said:
Even if it is in a text format, I would still have trouble pasting it directly onto the slide because all the methods to embed onto PowerPoint slide exposed by Microsoft.Office.Interop.PowerPoint.Shapes only accept the path of the file in string-not the file itself-as its parameter to access the required file... if I'm not mistaken.
This is not correct. I pointed out a way to create charts programmatically without having to create an excel sheet an save it somewhere.
If you would have gotten the data in a defined format (e.g. json) you could use my code to generate the chart.
But now what you seem to be waiting for:
If you click copy with and excel-chart selected, then open your PowerPoint presentation, set your selection where your want the chart to be and paste the chart, it will be inserted there no problem.
But how can I sent this Data and then paste it?
The Excel-Chart has got a method to copy itself:
https://learn.microsoft.com/de-de/office/vba/api/excel.chart(object)
Convert the data in your clipboard to binary data
Send the data
Convert the data back and store it in your clipboard
Use the TextRange of your Powerpoint-Selection to paste the data
https://learn.microsoft.com/de-de/office/vba/api/powerpoint.textrange
I have a CSV file and I want open it in an Excel and save it as an xlsx again. The reason I wanna do it this way, rather than write the data to an Excel file directly is that I hope it's faster that way since writing the data is very slow.
My csv looks like this:
sep=,
header1, header2, header3
data1, data2, data3
And here is my code to write the data to a CSV, open it in Excel and save it again:
var csvPath = [MyCSVPath];
File.WriteAllBytes(csvPath, WriteCsv(result));
excel.Workbooks.Open(csvPath, Delimiter: ",");
// Save the excel localy and load it's bytes again.
var path = [MyExcelPath]
workbook.SaveCopyAs(path);
excel.Workbooks.Close();
When I open the csv in the excel manually it is opened correctly. But when I do it over the code above it contains only the first line. What am I doing wrong?
Unfortunately, your code sample is not complete. But the following code works fine in Excel 2016 for your csv file example. By default SaveCopyAs doesn't change initial format, but SaveCopy allows to define format explicitly.
var excelApp = new Application();
excelApp.Workbooks.Open("d:\\1.csv", Delimiter: ",");
var workbook = excelApp.ActiveWorkbook;
// this saves in same CSV format
workbook.SaveCopyAs("d:\\2.xlsx");
// this saves to correct xlsx format
workbook.SaveAs("d:\\3.xlsx", XlFileFormat.xlOpenXMLWorkbook);
workbook.Close();
excelApp.Quit();
Sorry for my English. I used the library Epplus and I really like it. But I've got a problem: Out of Memory. Need to write large amounts of data, no matter what. I want to know is it possible to append to the end of the Excel file is not stored in the memory of all. Or create multiple files and then concatenate into one file. Thanks in advance.
1)if you retrieve your data from database
use a datareader instead of datatable
2)write the excel to a temp file, delete it after done(if it's web environment, use response.writefile then delete it)
3)write the header first then append data to it
something like this (using my phone to type this)
var pck = new ExcelPackage();
var ws = pck.AddSheet("sheet1");
//write header here
pck.saveas(fileinfo);
pck.dispose(); // not sure if function existed
pck= new excelpage(fileino.fullname);
ws = pck.worksheets[1];
var rowIndex =0;
while (reader.read())
{
if (++rowindex % 100000 == 0)
{
// save and re-open
}
//write row here
}
pck.save();
//dispose / send file / delete file etc
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.