How to embed images in Excel using C#? - c#

I currently have a list of Products in a SQL database, and one of the fields is a blob field that contains an image of the Product, I store it and retreive it using a Byte[] array, and the image is taken from PNG format. Displaying the images on the website when a user views a Product is working correctly.
What the customer now wants though is an export of all the products to Excel, including the images to be displayed in each row for each product, lets say Column F.
I've not been finding any information online that seems to work - sample code always seems to have an error or reference things that I can't find, which makes me wonder if there's versioning issues. I'm currently using Visual Studio 2010 and Office 2010 to test this.
I've seen some samples showing how to take the image to Clipboard and paste it, but considering that this will be running on a web server and might have to handle 20 or so products at a time, I'm not sure clipboard would be a good idea for a webserver?
I'd prefer to be able to write it into the excel file directly from the Byte[] array as I'm pulling all the other product data.
Before I update the code on the website, I'm trying to test it locally using a simple Console program. Once I know I can get an image into Excel, I'll probably write a dll library I can reference on the aspx page.
var xlApp = new Excel.Application();
Excel.Workbook xlWorkBook = xlApp.Workbooks.Add();
Excel.Worksheet xlWorkSheet = xlWorkBook.Worksheets[1];
byte[] imgdata = File.ReadAllBytes("product.png");
//what do I enter here to insert the image into cell[1,1]
xlWorkBook.SaveAs("abc.xlsx");
xlWorkBook.Close(true);
xlApp.Quit();
Marshal.ReleaseComObject(xlApp);

The key is to use worksheet paste function instead of using AddPicture if you want to use byte data instead of files on hard disk:
Bitmap bmp;
using (var ms = new MemoryStream(imgdata))
{
bmp = new Bitmap(ms);
System.Windows.Forms.Clipboard.SetDataObject(bmp, False);
var rng = xlWorkSheet.Range("A1");
xlWorkSheet.Paste(rng, bmp);
}

Related

Convert a Datatable to .xls,.xlsx,.csv by the given delimeter in input

I want a method to write the datatable data to .xls,.xlsx or.csv based on the input provided along with the delimiter as input
public class DataTableExtensions
{
/*Input Params : Datatable input
fileFormat(.xls,.csv,.xlsx)
delimeter('\t' (tabSpace) or ,(comma) or | (pipe Symbol)
filepath - Any local folder*/
public void WriteToCsvFile(DataTable dataTable,string fileFormat,string delimeter, string filePath)
{
//Code to convert file based on the input
//Code to create file
System.IO.File.WriteAllText(filePath, fileContent.ToString());
}
}
You said it is only 1000 rows every 2 hours in the comments. That is a acceptable amount of data for a C# programm. I would say the big question left is wich output format you use.
.CSV is the simplest one. This format can be done with a File.WriteLine() and some string concaction. There is no build in CSV parser or writer code I am aware off in C#, but there is plenty of 3rd party code.
.XLS requires the (t)rusty Office COM Interop. That requires office to be installed and does not work from a non-interactive session (like a Windows Service). On top of all the normal issues for using COM interop.
There is the odd "export to XLS" function on existing classses, but those are rare, far inbetween and about everything you get. Unfortunately as we always had COM Interop as fallback, we never quite developed a standalone library for working with .XLS. Ironically working with this old format is harder from C#/.NET then it would be from Java.
.XLSX however is easier. It can be written using the OpenXML SDK. Or the XML writer and ZipArchive class: At their core all the ???x formats are a bunch of .XML files in a renamed .ZIP container. There should even be 3rd party code out there to make using the SDK easier.
.CSV is the lowest common denominator and propably the easiest to create. However if a user is supposed to open this document, the lack for formating might become an issue.
.XSLX would be my choice if you need a user to open it.
.XSL I would avoid like a swarm of angry bees.
I have written this Program to convert Xls,XLSx using console application with
Datatable as input and for text file I have written a simple stream writer logic.This works good. Initially I have installed package manage console and below code
using expertXLs package.I am not sure wheather I can share the key of that
or not.Please search the key and give in config before running it
Package Manage Console - Install-Package ExpertXls.ExcelLibrary -Version 5.0.0
Code :
--------
private static void GenerateTxtFileFromDataTable(DataTable sampleDataTable,string delimiter)
{
var _expertxlsLK = ConfigurationManager.AppSettings["ExpertxlsLK"];
//GetKey Value from config
// Create the workbook in which the data from the DataTable will be loaded 0 for 2003 Excel(xls),1 for 2007 Excel(xlsx)
ExcelWorkbookFormat workbookFormat = ExcelWorkbookFormat.0;
// create the workbook in the desired format with a single worksheet
ExcelWorkbook workbook = new ExcelWorkbook(workbookFormat);
workbook.EnableFormulaCalculations();
workbook.LicenseKey = _expertxlsLK;
// get the first worksheet in the workbook
ExcelWorksheet worksheet = workbook.Worksheets[0];
// set the default worksheet name
worksheet.Name = "ClaimInformation";
// load data from DataTable into the worksheet
worksheet.LoadDataTable(sampleDataTable, 1, 1, true);
worksheet.Workbook.EnableFormulaCalculations();
workbook.Save(#"M:\Rupesh\test.xlsx");
workbook.Close();
}

How to export Chart created in EPPlus to Image file

I have tried this;
var excelPicture = sheet.Drawings[0] as OfficeOpenXml.Drawing.ExcelPicture;
var img = excelPicture.Image;
However the excelPicture variable become null. How can I create image file from ExcelDrawing??
It seems that this can't be done via EPPlus API. There is the opened issue in the GitHub project repository how export the drawings to a file. But the provided solution in the question does not work (it seems #Onchomngebul himself wrote the the comment)
Alternative solution is to use Workbook class in Spire.XLS nuget-package (or via free version FreeSpire.XLS).
var workbook = new Workbook();
workbook.LoadFromFile(workbookFileName, ExcelVersion.Version2010);
var sheet = workbook.Worksheets[0]; // index or name of your worksheet
var image = workbook.SaveChartAsImage(sheet, 0); // chart index
img.Save(chartFileName, ImageFormat.Png);
For more details please see this comment in the issue.
Another solution is to try to use Excel.ChartObject. I think this question Exporting Excel Charts as Images may help you.

How to open Image manager through Enterprise Architect API

I am working on Enterprise Architect through C# add-ins. I need to display the image manager through automation where user can add directly add images on an "add image" button click in form.
I use the API Repository.InvokeConstructPicker() but it only opens the select package/class/component window. Is there an EA API available to open the Image Manager.
No, there is none. There is the undocumented Respository.CustomCommand which can open several properties windows. But the image manager is not part of that (or it has not been discovered what parameters to supply).
Please see Edit2 below about adding new values to the table.
Edit: Based on another question I had to dig into this a bit deeper.
I found out that, although EA imports a number of different image formats, it internally uses PNG to store the image. Obviously their BMP-importer does not like all BMP formats (not so deep in that, but I seem to remember there's some 8/16 bit stuff; typical Windoze weirdness). Anyhow, I used this Python code snippet to retrieve some test image data, previously imported into EA:
import sys
import win32com.client
import base64
import xml.etree.ElementTree
eaRep = None
try:
eaApp = win32com.client.GetActiveObject(Class="EA.App")
eaRep = eaApp.repository
except:
print "failure to open EA"
sys.exit(2)
def dump():
sqlRes = eaRep.SQLQuery("SELECT * FROM t_image")
root = xml.etree.ElementTree.fromstring(sqlRes)
for dataset in root:
for data in dataset:
for row in data:
name = row[1].text
print name
data = row[3].text
png = base64.standard_b64decode(data)
file = open("c:/" + name + ".png", "wb")
file.write(png)
file.close()
dump()
This correctly extracted the images from the database.
Edit2: I was assuming that EA stores the png as base64, but that's not true. EA only delivers base64 on return of SQLQuery. But they internally just store the raw png in Image. So, unfortunately, you can not use Repository.Execute since it can not transport binary data - or at least I have not figured out how to do that. As a work around you can look into Repository.ConnectionString and open a native connection to the database. Once you have plugged the new picture(s) in the table you can use them via thier ImageID.
Contents of t_image:
ImageID : You just need to create an unique ID
Name : an arbitrary string
Type : fixed string Bitmap
Image : blob of a png
Here's a Python snippet that connects natively to an EAP file:
import pyodbc
db_file = r'''C:\Documents and Settings\Administrator\Desktop\empty.eap'''
odbc_conn_str = 'DRIVER={Microsoft Access Driver (*.mdb)};DBQ=' + db_file
conn = pyodbc.connect(odbc_conn_str)
cursor = conn.cursor()
cursor.execute("select * from t_image")
row = cursor.fetchone()
if row:
print(row)
Rather than printing the row with the image data (which shows that its contents is a png-blob) you can use it to actually issue an INSERT or UPDATE to modify t_image.

Excel Interop Picture not displaying

hopefully you guys can help me here. I have this excel interop picture code that seems to be only working on my end. The picture is in a folder in the mapped network drive. When I run the code and receive the excel file, the picture is on the file.
Microsoft.Office.Interop.Excel.Range picRange = xlWorkSheet.get_Range("A1:G1");
picRange.Merge(Type.Missing);
Microsoft.Office.Interop.Excel.Pictures p = xlWorkSheet.Pictures(misValue) as Microsoft.Office.Interop.Excel.Pictures;
Microsoft.Office.Interop.Excel.Picture pic = null;
pic = p.Insert(Server.MapPath("~/images/letter.gif"), misValue);
pic.Left = 87;
pic.Top = Convert.ToDouble(picRange.Top);
pic.Height = 80.25;
pic.Width = 320;
pic.Placement = Microsoft.Office.Interop.Excel.XlPlacement.xlFreeFloating;
However, if I run the code on a different computer, which also has access to that mapped network drive, it the excel file comes with an error on the image that says:
the linked image cannot be displayed. The file may have been moved
renamed or deleted
I've checked three times and made sure that the image file is the right name and the right place. But it only seems to work locally on my computer.
Is there a function in the excel interop library where I can create an instance of the image instead of linking to the image file? Any help will do.

Can an Excel Document be Loaded into Memory, Populated, and Returned as Bytes?

I've worked with Excel Interop before for the purposes of opening an Excel file, writing some data to it, and saving it. Something like this:
public void WriteAFile()
{
var xlApp = new Excel.ApplicationClass();
var xlWorkbook = xlApp.Workbooks.Add(Missing.Value);
var xlWorksheet = (Excel.Worksheet)xlWorkbook.Worksheets.get_Item(1);
xlWorksheet.Name = "Some Name";
// Write some contrived data
for (var i = 0; i < 10; i++)
{
xlWorksheet.Cells[i + 1, 1] = i;
xlWorksheet.Cells[i + 1, 2] = i + 1;
//... and so on...
}
//... and so on...
// Save the file
xlWorkbook.SaveAs(
#"",
Excel.XlFileFormat.xlWorkbookNormal,
Missing.Value,
Missing.Value,
false,
false,
Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlNoChange,
Excel.XlSaveConflictResolution.xlLocalSessionChanges,
false,
Missing.Value,
Missing.Value,
Missing.Value
);
xlWorkbook.Close(true, Missing.Value, Missing.Value);
xlApp.Quit();
// release other resources, etc.
}
This works reasonably well for an application writing to a file on a local workstation. However, now I'm working on a web application which needs to return a file to the user. The catch is that the file on the file system can't be modified. So what I need to do is:
Load a known Excel file into memory (note that the file is .xlsm and contains macro code, which will be run on the user's workstation when they download it).
Write known data to a known location in the file (for the sake of argument, some numeric values to the first few cells on sheet 99).
Stream that file from memory via the web application (the streaming part is trivial, it's getting the byte array in the first place that seems trickier to me).
I've Googled around, but haven't found much of anything. Basically just instructions and examples which do what I'm already doing in my code above. It also comes as no surprise when working with COM interop that my code above (which is pretty old) and other code I find online is all a bit dated. We're using .NET 4.0, so anything that's available there is appropriate for us.
The extent of my experience with COM is pretty much fully displayed in the code above. I'd prefer not to use COM at all for this, if that's at all possible. But right now I'm just having trouble getting off the ground. Can anybody point me in the right direction? Maybe reference some classes or methods that would be of particular use here? Thanks.
P.S. Another thing that kind of frightens me is the need for Excel on the web server, as well as the possibility of the Excel application opening on the console of the web server and hanging on a user-unseen error of some kind. So any advice there is very much appreciated.
I ended up using the ClosedXML library. (OpenXML by itself was... unwieldy.) As a simple proof of concept for opening the file, writing a piece of data, and returning a byte array:
public byte[] GetExcelApplication(Guid sessionKey)
{
// Input checking
using (var workbook = new XLWorkbook(_excelFile))
{
var worksheets = workbook.Worksheets.Where(w => w.Name == "Session Key");
if (worksheets.Count() < 1)
throw new Exception("Excel file does not contain a worksheet called: Session Key");
if (worksheets.Count() > 1)
throw new Exception("Excel file contains multiple worksheets called: Session Key");
var worksheet = worksheets.Single();
var cell = worksheet.Cell("A1").Value = sessionKey.ToString();
using (var ms = new MemoryStream())
{
workbook.SaveAs(ms);
return ms.ToArray();
}
}
}
With c# 4.0 COM should be a bit easier since you can use dynamic. But it's then still COM.
I use a thirt party product for this called aspose cells. I'm not working for them, but i realy like this product(even if it's not free). This save me a lot of trouble when dealing with excel.
Old-school, but I wrote this a long time ago that constructs a workbook on the fly and exports it. Maybe it can get you on the right track?
Doing something similar with COM I resorted to saving the generated Excel workbook to a temporary folder, sending it off and then deleting it. Not using COM wasn't really an option for me as the workbook also needed pivot tables and graphs generated.

Categories