stream to excel without saving - c#

I am creating excel and filling values into it. i am using EPPlus.
I can save it using filestream and open it.
but i want to open it without saving.
I thought we can use memorystream in some way to generate the excel directly.
Please guide.
try
{
MemoryStream newFile = new MemoryStream();
using (ExcelPackage package = new ExcelPackage(newFile))
{
// Add Data Collection worksheet
ExcelWorksheet dataWorksheet = package.Workbook.Worksheets.Add("Sheet1");
dataWorksheet.Cells[1, 1].Value = " My Text";
dataWorksheet.Cells[1, 1].Style.Font.Size = 14;
dataWorksheet.Cells[3, 1].Value = BALGlobalVariables.cocName;
dataWorksheet.Cells[5, 1].Value = "IR From Date :";
dataWorksheet.Cells[6, 1].Value = "IR To Date : ";
dataWorksheet.Cells[5, 6].Value = "From Date :";
dataWorksheet.Cells[6, 6].Value = "To Date : ";
dataWorksheet.Cells[5, 2].Value = fromDate;
dataWorksheet.Cells[6, 2].Value = toDate;
dataWorksheet.Cells[5, 7].Value = invFromDate;
dataWorksheet.Cells[6, 7].Value = invFromDate;
// Template specific excel generation goes in here
FillPurchaseExcelData(ref dataWorksheet, masterTable, subTable);
// save package
package.Save();
}
byte[] fileContent = newFile.ToArray();
#if DEBUG
string tempName = "MTemp.xlsx";
string tempFileName = System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]) + #"\" + tempName;
//Write the stream data of workbook to the root directory
using (FileStream file = new FileStream(tempFileName, FileMode.Create))
{
file.Write(fileContent, 0, fileContent.Length);
}
System.Diagnostics.Process.Start(tempFileName);

You can generate excel file and choose not to save it, but Excel can't open files that are not saved on disk (it becomes a 'file' when it's on disk), so in short - you have to save document in order to open it in Excel.
If you're concerned about storing data on hard drive, as an alternative you could create a data source on a medium you trust, and then create an xslx that would instruct Excel to consume that data source when opening worksheet (instead of creating a file pre-filled with data). But this is a whole other story...

Related

Saving excel file in user's downloads folder producing error (.NET Core Razor Pages)

In my razor page application, I have a button that you click which creates an excel file and should automatically save it to your downloads folder.
The code below works great in localhost - I click the button, it saves to MY downloads folder, and I can view it.
However, once I publish and try, I receive an error that states "Could not find a part of the path 'C:\WINDOWS\system32\config\systemprofile\Downloads\PartCommentHistory.xlsx'.".
I would also be perfectly fine changing this code to instead pull up the save file dialog window and allow the user to pick where the file gets saved in the first place - but I'm not sure how. Google isn't helping much, so here we are!
If I physically navigate to this path, I noticed that there is no Downloads folder. I tried adding an if statement in my code that says if the Downloads folder doesn't exist here, create it first and then save the file there. However, that produces another error which is that I don't have access to the path.
public async Task<IActionResult> OnPostExportAsync(string currentFilter)
{
string sFilePath = Path.Combine(Environment.ExpandEnvironmentVariables("%USERPROFILE%"),"Downloads");
string sFileName = #"PartCommentHistory.xlsx";
string URL = string.Format("{0}://{1}/{2}", Request.Scheme, Request.Host, sFileName);
FileInfo file = new FileInfo(Path.Combine(sFilePath, sFileName));
var memory = new MemoryStream();
using (var fs = new FileStream(Path.Combine(sFilePath, sFileName), FileMode.Create, FileAccess.Write))
{
ExcelPackage pck = new ExcelPackage();
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Worksheet1");
List<CmtPartComment> commentlist = _context.CmtPartComments.Select(x => new CmtPartComment
{
SupplierNo = x.SupplierNo,
PartNo = x.PartNo,
Comment = x.Comment,
EnterBy = x.EnterBy,
EnteredDt = x.EnterDt.ToString("yyyy-MM-dd HH:mm:ss tt"),
CompletedDt = x.CompleteDt.ToString("yyyy-MM-dd HH:mm:ss tt")
}).Include(c => c.System).OrderByDescending(x => x.EnterDt).Where(x => x.PartNo == currentFilter).ToList();
ws.Cells[1, 1].Value = "SupplierNo";
ws.Cells[1, 2].Value = "PartNo";
ws.Cells[1, 3].Value = "Comment";
ws.Cells[1, 4].Value = "EnterBy";
ws.Cells[1, 5].Value = "EnterDt";
ws.Cells[1, 6].Value = "CompleteDt";
int recordIndex = 2;
foreach (var item in commentlist)
{
ws.Cells[recordIndex, 1].Value = item.SupplierNo;
ws.Cells[recordIndex, 2].Value = item.PartNo;
ws.Cells[recordIndex, 3].Value = item.Comment;
ws.Cells[recordIndex, 4].Value = item.EnterBy;
ws.Cells[recordIndex, 5].Value = item.EnteredDt;
ws.Cells[recordIndex, 6].Value = item.CompletedDt;
recordIndex++;
}
ws.Cells["A:AZ"].AutoFitColumns();
pck.SaveAs(fs);
}
using (var stream = new FileStream(Path.Combine(sFilePath, sFileName), FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", sFileName);
}
For your issue, it is caused by that you are creating a temp file in the server side by using (var fs = new FileStream(Path.Combine(sFilePath, sFileName), FileMode.Create, FileAccess.Write)) which may not exist in the server side.
For your requirement, you are trying to create a file and return it to client side. If so, there is no need to create the local file in the server side, you could return the byte of the file like below:
public async Task<IActionResult> OnPostExportByInMemoryAsync(string currentFilter)
{
string sFileName = #"PartCommentHistory.xlsx";
using (var pck = new ExcelPackage())
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Worksheet1");
ws.Cells[1, 1].Value = "SupplierNo";
ws.Cells[1, 2].Value = "PartNo";
ws.Cells[1, 3].Value = "Comment";
ws.Cells[1, 4].Value = "EnterBy";
ws.Cells[1, 5].Value = "EnterDt";
ws.Cells[1, 6].Value = "CompleteDt";
ws.Cells["A:AZ"].AutoFitColumns();
return File(pck.GetAsByteArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", sFileName);
}
}
Use this method to get a folder path
Environment.GetFolderPath(Environment.SpecialFolder.Yourspecialfoldernamehere, System.Environment.SpecialFolderOption.None)
For example
Environment.GetFolderPath(Environment.SpecialFolder.System));
In above example System is a special folder.
You can't determine where on the client machine the file can be saved. The only reason it appears to work on your machine is because your machine acts as the server. All you can do is to force a Save or Open dialog when the user downloads the file, which is achieved by setting the content type to application/octet-stream:
Do I need Content-Type: application/octet-stream for file download?

Read Excel file from memory stream in C# Console application

I am fetching an Excel file from ftp and getting that file in a memory stream. I have to read that file from memory stream. I tried through Excel Interop but it is not accepting memory stream as a parameter in
xlWorkBook = xlApp.Workbooks.Open(strm, 0, true, 5, "", "", true,
Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
According to system requirement that I cannot save that file temporary; because I am using Azure web jobs for Console application deployment. Is there any way to read file from memory stream or can I convert that memory stream into an array of string?
I can suggest you to use ExcelDataReader 3.1.0 to read data from an Excel file.
Now you can use that MemoryStream in ExcelReader like this:
Note that reader of old Excel files -.xls- is different form newer files -.xlsx-.
var excelReader = originalFileName.EndsWith(".xls")
? ExcelReaderFactory.CreateBinaryReader(stream)
: ExcelReaderFactory.CreateOpenXmlReader(stream);
If you want to extract a string from your MemoryStream you can use a StreamReader:
var streamReader = new StreamReader(memoryStream);
var stringResult = streamReader.ReadToEnd();
If you want to work over a FileStream you can copy your MemoryStream to it like this:
memoryStream.CopyTo(fileStream);
Also EasyXLS accepts streams, including MemoryStream.
I don't know if you need only data in cells from Excel, or other information, but the code bellow is only for data:
ExcelDocument excelWorkbook = new ExcelDocument();
DataSet ds = excelWorkbook.easy_ReadXLSActiveSheet_AsDataSet(memoryStream);
More details about reading Excels, you can find at this location:
https://www.easyxls.com/manual/FAQ/read-excel-file-in-dot-net.html
There is no MS Office present in the Azure Webjob, so we cannot use Microsoft.Office.Interop Dll in the Azure Webjob. Please have a try to use DocumentFormat.OpenXml to do that. The following is the demo code from the official document. I also find another tutorials about how to Read and Write Microsoft Excel with Open XML SDK.
public static void OpenAndAddToSpreadsheetStream(Stream stream)
{
// Open a SpreadsheetDocument based on a stream.
SpreadsheetDocument spreadsheetDocument =
SpreadsheetDocument.Open(stream, true);
// Add a new worksheet.
WorksheetPart newWorksheetPart = spreadsheetDocument.WorkbookPart.AddNewPart<WorksheetPart>();
newWorksheetPart.Worksheet = new Worksheet(new SheetData());
newWorksheetPart.Worksheet.Save();
Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.GetFirstChild<Sheets>();
string relationshipId = spreadsheetDocument.WorkbookPart.GetIdOfPart(newWorksheetPart);
// Get a unique ID for the new worksheet.
uint sheetId = 1;
if (sheets.Elements<Sheet>().Count() > 0)
{
sheetId = sheets.Elements<Sheet>().Select(s => s.SheetId.Value).Max() + 1;
}
// Give the new worksheet a name.
string sheetName = "Sheet" + sheetId;
// Append the new worksheet and associate it with the workbook.
Sheet sheet = new Sheet() { Id = relationshipId, SheetId = sheetId, Name = sheetName };
sheets.Append(sheet);
spreadsheetDocument.WorkbookPart.Workbook.Save();
// Close the document handle.
spreadsheetDocument.Close();
// Caller must close the stream.
}

ASP.NET MVC Excel export file format error

I am currently writing an ASP.NET MVC 5 controller action to export some data to an Excel file (using some code I found here). It works...mostly. It outputs an Excel file, which I can open, but not before displaying the following error message:
"The file format and extension of 'Export.xls' don't match. The file could be corrupted or unsafe. Unless you trust it's source, don't open it. Do you want to open it anyway?"
I go on to select "Yes" and then it opens. I can resave it on my machine and open that file and I don't get the error. It otherwise works fine, the only other oddness is that the file's gridlines are formatted differently than is usual for an Excel file, almost more like an HTML table than an Excel sheet. However, the weird error message isn't something that would be acceptable here, so I have to fix it.
Here is my code:
public void ExportExcel()
{
// DataObject is a class that fetches the data for this method
DataObject dataObj = new DataObject();
var grid = new GridView();
// dataObj.GetDataList returns a List<T> of data model class objects
grid.DataSource = dataObj.GetDataList();
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=Export.xls");
Response.ContentType = "application/vnd.ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
StreamReader sr = new StreamReader(s, Encoding.ASCII);
Response.Write(sr.ReadToEnd());
Response.End();
}
I have already tried setting the Response.ContentType to other values ("application/excel", "application/ms-excel"), to no avail. I'm a little new to ASP.NET and C# in general, so there might be something I'm missing or doing wrong here; I'm more used to PHP and Zend. Any insight or help you could give would be greatly appreciated; thanks!
You're writing an HTML table as an Excel file. Basically, you're taking text with this:
<table>
<tr>
<td>
Stuff
</td>
</tr>
</table>
and writing it as a text file with a .xls extension. Excel is "smart" enough (if you can call it that) to open the file and display it properly, although it alerts you that the file isn't actually an xls file first.
You need to either deal with it (not a good solution), convert the data in the table to a csv and send a CSV (a much better solution) or use an Excel library to create an actual Excel file and send that. Of those, the CSV is probably the easiest.
i got the same error. So, I started (using Microsoft.Office.Interop.Excel) get it from Nuget Manager.
Here is the code
Microsoft.Office.Interop.Excel.Application xla = new Microsoft.Office.Interop.Excel.Application();
Workbook wb = xla.Workbooks.Add(XlSheetType.xlWorksheet);
Worksheet ws = (Worksheet)xla.ActiveSheet;
/*Headers here */
ws.Cells[1, 1] = "Header1";
ws.Cells[1, 2] = "Header2"; ws.Cells[1, 3] = "Header3"; ws.Cells[1, 4] = "Header4"; ws.Cells[1, 5] = "Header5";
int i = 2;
/* use your list here to fill the data rows */
foreach (var a in pavmm.Funds)
{
ws.Cells[i, 1] = a.FilingID;
ws.Cells[i, 2] = a.Security_Name; ws.Cells[i, 3] = a.Filing_Type; ws.Cells[i, 4] = a.st_name; ws.Cells[i, 5] = a.Permit;
i = i + 1;
}
ws.Columns.AutoFit();
ws.Rows.AutoFit();
string folder = NewFolderName + "\\";
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
string filename = ExcelsheetName + ".xlsx";
filename = filename.Replace('/', '-');
filename = filename.Replace('\\', '-');
string path = Path.Combine(folder, filename);
wb.SaveCopyAs(path);

Opening a client-side Excel file using EPPlus

I am the middle of writing a program which has to create an excel file on a client machine and then, after they have saved and updated it then I need to import it again and apply the changes to my database. The export bit of it works fine, however when I import from the web application I get zero worksheets in my workbook. If I run it on my development machine, i.e. the website is running on the same machine as the file is located then it works fine.
My code is as follows:
try
{
Productsfile = new FileInfo(PathandFileName);
}
catch (Exception ex)
{
lblStatus.Text = string.Format("Error getting file info {0}", ex.Message);
return;
}
lblStatus.Text += "File Opened succesfully " + Productsfile.FullName + Environment.NewLine;
//Open and read file
try
{
using (ExcelPackage datafile = new ExcelPackage(Productsfile))
{
ExcelWorkbook wkb = datafile.Workbook;
if (wkb != null)
{
lblStatus.Text += string.Format("The workbook is not null and there are {0} worksheets", wkb.Worksheets.Count) + Environment.NewLine;
I have confirmed that the code gets to the last label update, which tells me there are zero worksheets in the workbook, but importantly that the workbook itself is not null.
When we created the export the following code was used, which transfers the data stream from server to client - I am wondering if something to reverse it is needed:
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", "attachment; filename=Products.xlsx");
Response.BinaryWrite(pck.GetAsByteArray());
Response.End();
Right, I believe I've found the answer:
1) Get the file details from a fileupload control
2) Convert this to a memory stream
3) Create the package using the memory stream
And the code is as follows:
if (FileUpload1.HasFile)
{
HttpPostedFile file = Request.Files[0];
myLabel.Text = file.FileName;
MemoryStream mem = new MemoryStream();
mem.SetLength((int)file.ContentLength);
file.InputStream.Read(mem.GetBuffer(), 0, (int)file.ContentLength);
ExcelPackage package = new ExcelPackage(mem);
ExcelWorksheet worksheet = package.Workbook.Worksheets[1];
myLabel.Text += " " + worksheet.Name;
}

Reading memory stream

I'm trying to create an excel file and make that available as download. But i have some trouble with the download part.
I'm using EPPlus to create an xml file. I've got that working. But just local. I'm not sure how to force the download of the file.
This is my code:
public Stream GetXlsxDocument(IQueryable data)
{
const string sheetName = "Sheet1";
var localFile = new FileInfo(#"C:\test2.xlsx");
var file = new FileInfo("test2.xlsx");
// Used for local creation
//ExcelPackage p = new ExcelPackage();
MemoryStream stream = new MemoryStream();
using (ExcelPackage p = new ExcelPackage(stream))
{
p.Workbook.Worksheets.Add("Sheet1");
ExcelWorksheet ws = p.Workbook.Worksheets[1];
ws.Name = sheetName;
ws.Cells.Style.Font.Size = 11;
ws.Cells.Style.Font.Name = "Calibri";
ws.SetValue(1, 1, "aaa"); // Test data
// Used for local creation
//p.SaveAs(localFile);
p.SaveAs(stream);
}
return stream;
}
Like i said before. Creating the xlsx file locally on my C:\ disk works. But how can i force the download of the created xlsx file?
Right now its giving me an xlsx file of 0 bytes. I'd need to return a stream which isn't empty. Anyone any idea how i can do this..??
rewind it:
stream.Position = 0;
return stream;

Categories