I'm using EPPlus to create an excel file on the server. The problem is that I wan't the file to be saved on the clients harddrive and when I the application up on a server I believe this will save the file on the server harddrive.
Is it possible to send this file back to the client/browser some how?
public void CreateAnnuityExcelSheet(List<Calculation> cList, FormCollection form, int DTCyear)
{
List<Calculation> newList = new List<Calculation>();
newList.Add(cList.First()); //Getting the values for the first row
var StartValue = newList[0].StartValue;
var radio = form["advanceOrArrears"];
string fileName = newList[0].CalculationName;
string path = #"C:\ExcelFiles\" + fileName + ".xlsx"; //Path for the file
FileInfo info = new FileInfo(path);
info.Directory.Create(); //If C:\ExcelFiles does not exist, create it
if (!info.Exists)
{
using (ExcelPackage package = new ExcelPackage(info))
{
ExcelWorksheet ws = package.Workbook.Worksheets.Add(fileName);
//Styles for the sheet
package.Save();
}
}
}
The easiest way would be to send the bytes as File to browser. If your library for creating Excel files allows you to save to stream (like for example ClosedXML does) then you can do in your MVC action
var stream = new MemoryStream();
workbook.SaveAs(stream);
stream.Position = 0;
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml", "file.xlsx");
If you can't save it to memory stream then save it to server's disc and then you can just pass file path and content type to return File().
I've used a httphandler for sending the byte file object to the browser.
This link should help, Generating a file, then launching a secure download
So save the file on the server then transmit it to the user in your controller:
return new FilePathResult(myFilePath,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
Related
My application needs to save a byte[] to disk, the issue is that I know it is an excel file but not what the correct file extension is. Is there a way I can tell if the file is .xls or .xlsx or .xlsm from the data itself?
I ended up writing an extension method to determine the excel file type. This method is not perfect. It will only correctly detect a .xlsm file if the file has a macro.
private static string FindType(this byte[] file)
{
using(MemoryStream ms = new MemoryStream(file))
{
var zf = new ZipArchive(ms, ZipArchiveMode.Read);
if (zf.Entries.Any(e=>e.FullName.StartsWith("xl.")))
{
if (zf.Entries.Any(e=>e.FullName.Equals("xl/vbaProject.bin", StringComparison.InvariantCultureIgnoreCase)))
return ".xlsm";
else
return ".xlsx";
}
}
return string.Empty;
}
I am facing issue while downloading/exporting XML file from C# model to local machine of browser(I have front end for it).
However I am able to download/export the file from C# model to XML and save it on directory on server.
I am using below code for it :
var gradeExportDto = Mapper.Map<GradeExportDto>(responseGradeDto);
System.Xml.Serialization.XmlSerializer writer = new System.Xml.Serialization.XmlSerializer(gradeExportDto.GetType());
var path = _configuration.GetValue<string>(AppConstants.IMPORT_EXPORT_LOCAL_URL) + "\\"+ responseGradeDto.Code+"_"+DateTime.UtcNow.ToString("yyyy-MM-dd") + ".xml";
System.IO.FileStream file = System.IO.File.Create(path);
writer.Serialize(file, gradeExportDto);
file.Close();
Angular Code :
onExport(selectedData: any): void{
this.apiService.post(environment.api_url_master, 'ImportExport/ExportGrade/', selectedData).subscribe(result => {
this.translateService.get('GradeExportSuccess').subscribe(value => this.toastr.success(value));
}, err => {
this.toastr.error(err.message);
});
}
I need help in getting this file downloaded to local system on which browser is running.
Please let me know if more information is required from my side.
NOTE : I am not trying to download existing file. I have model in C# which I need to convert in XML and then download it to my local. However I am able to convert it to XML but not able to download on local.
You cannot save anything directly to a client machine. All you can do is provide the file as a response to a request, which will then generally prompt a download dialog on the client, allowing them to choose to save it somewhere on their local machine.
What #croxy linked you to is how to return such a response. If the issue is that the answer is using an existing file, you can disregard that part. The idea is that you're returning a byte[] or stream, regardless of where that's actually coming from. If you're creating the XML in memory, then you can simply do something like:
return File(memoryStream.ToArray(), "application/xml", "file.xml");
Instead of serializing your data into a file, serialize it into a stream eg. MemoryStream and return a File() from your action:
public IActionResult GetXml()
{
var gradeExportDto = Mapper.Map<GradeExportDto>(responseGradeDto);
var writer = new System.Xml.Serialization.XmlSerializer(gradeExportDto.GetType());
var stream = new MemoryStream();
writer.Serialize(stream, gradeExportDto);
var fileName = responseGradeDto.Code + "_" + DateTime.UtcNow.ToString("yyyy-MM-dd") + ".xml";
return File(stream.ToArray(), "application/xml", fileName);
}
I'm calling the action "Export" where i pass a list of viewmodels and define the format
public ActionResult DownloadTokenlist(string startDate = null, string endDate = null)
{
using (HRCTSStatisticDb db = new HRCTSStatisticDb(Setting.ClientId))
{
List<TokenExportViewModel> tokenExportViewModels = new List<TokenExportViewModel>();
Response.AddHeader("content-disposition", $"attachment;filename=Tokenlist_{DateTime.Now.ToString("dd.MM.yyyy")}.xlsx");
log.InfoFormat($"The {new HomeController().UserRole(Context.LoggedInUser)}: {Context.LoggedInUser} has used the exceldownload");
return File(new ExcelExport().Export(tokenExportViewModels), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}
}
The action i call (ExcelEngine is by Syncfusion):
public MemoryStream Export(List<TokenExportViewModel> list)
{
MemoryStream stream = new MemoryStream();
using (ExcelEngine excelEngine = new ExcelEngine())
{
IApplication application = excelEngine.Excel;
application.DefaultVersion = ExcelVersion.Excel2010;
IWorkbook workbook = application.Workbooks.Create(1);
IWorksheet worksheet = workbook.Worksheets.Create("Tokenlist");
IStyle defaultStyle = workbook.Styles.Add("default");
defaultStyle.Font.Size = 12;
worksheet.SetDefaultColumnStyle(1, 20, defaultStyle);
worksheet.SetDefaultRowStyle(1, 300, defaultStyle);
worksheet.UsedRange.AutofitColumns();
worksheet.Range["A1"].Text = $"Tokenlist - {DateTime.Today.ToString("dd.MM.yyyy")}";
worksheet.Range["A1"].CellStyle = h1Style;
workbook.SaveAs(stream);
workbook.Close();
}
return stream;
}
I only posted the code which has an impact on the file and (maybe) could create the error.
There is no error, until i open the file, then this exception pops up:
Excel cannot open the file 'Tokenlist_22.05.2018.xlsx' because the
file format or file extension is not valid. Verify that the file has
not been corrupted and that the file extension matches the format of
the file.
I've tried to change the file format to .xls and .vbs but neither works. With .xls I can open the document but then it has no data in it.
The .close() doesn't change much, it just closes the output stream previously opened.
As stream reached end position while returning it, the downloaded file is getting corrupted. So, it is recommended to set its current position to 0 to resolve this issue. Please refer below code to achieve the same.
Code Example:
workbook.SaveAs(stream);
workbook.Close();
stream.Position = 0;
We have also shared a simple sample for your reference which can be downloaded from following link.
Sample Link: http://www.syncfusion.com/downloads/support/directtrac/general/ze/Sample1020485770.zip
I work for Syncfusion.
Use the FileContentResult Overload where you can provide thefileDownloadName like this:
return File(excelExport.Export(tokenExportViewModels).ToArray(),"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"Tokenlist_{DateTime.Now.ToString("dd.MM.yyyy")}.xlsx");
And use the stream ToArray() extension to return a byte[].
(I assume your Export method is generating a valid document)
This is my working code that I used to download multiple files as a zip file using Ionic.Zip dll. File contents is stored in a SQL database. This program works if I try to download 1-2 files at a time, but throws an OutOfMemory exception if I try to download multiple files as some of the files may very large.
Exception occurs when it's trying to write in to outputStream.
How can I improve this code to download multiple files or is there a better way to download multiple files one by one rather than zipping them to a one large file?
Code:
public ActionResult DownloadMultipleFiles()
{
string connectionString = "MY DB CONNECTIOBN STRING";
List<Document> documents = new List<Document>();
var query = "MY LIST OF FILES - FILE METADA DATA LIKE FILEID, FILENAME";
documents = query.Query<Document>(connectionString1).ToList();
List<Document> DOCS = documents.GetRange(0, 50); // 50 FILES
Response.Clear();
var outputStream = new MemoryStream();
using (var zip = new ZipFile())
{
foreach (var doc in DOCS)
{
Stream stream = new MemoryStream();
byte[] content = GetFileContent(doc.FileContentId); // This method returns file content
stream.Write(content, 0, content.Length);
zip.UseZip64WhenSaving = Zip64Option.AsNecessary // edited
zip.AddEntry(doc.FileName, content);
}
zip.Save(outputStream);
}
return File(outputStream, "application/zip", "allFiles.zip");
}
Download the files to disc instead of to memory, then use Ionic to zip them from disc. This means you don't need to have all the files in memory at once.
I have written a procedure that will open a xls from a local disc, refresh the data in it and then save it again. This works fine.
The problem occurs when I replace the filename to point to a SharePoint site. It opens the file fine. Refreshes the file, but when it trys to save the file it throws an exception with the message "Cannot save as that name. Document was opened as read-only.".
If I try and save the file with a different filename then it works fine.
Does anybody know what I am missing? I think it must have somethoing to do with how I am opening the file. Is there another way that I can force the opening of the file in a read/write manner?
private static void RefreshExcelDocument(string filename)
{
var xls = new Microsoft.Office.Interop.Excel.Application();
xls.Visible = true;
xls.DisplayAlerts = false;
var workbook = xls.Workbooks.Open(Filename: filename, IgnoreReadOnlyRecommended: true, ReadOnly: false);
try
{
// Refresh the data from data connections
workbook.RefreshAll();
// Wait for the refresh occurs - *wish there was a better way than this.
System.Threading.Thread.Sleep(5000);
// Save the workbook back again
workbook.SaveAs(Filename: filename); // This is when the Exception is thrown
// Close the workbook
workbook.Close(SaveChanges: false);
}
catch (Exception ex)
{
//Exception message is "Cannot save as that name. Document was opened as read-only."
}
finally
{
xls.Application.Quit();
xls = null;
}
}
Many thanks in advance for suggestions.
Jonathan
Unfortunately you can't save directly to SharePoint using the Excel API. That's why the file is being opened as read only - it's not allowed.
The good news is that it is possible, but you have to submit the form via a web request. Even better news is that there is sample code on MSDN! In particular notice the PublishWorkbook method that sends a local copy of the Excel file to the server via a web request:
static void PublishWorkbook(string LocalPath, string SharePointPath)
{
WebResponse response = null;
try
{
// Create a PUT Web request to upload the file.
WebRequest request = WebRequest.Create(SharePointPath);
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = "PUT";
// Allocate a 1K buffer to transfer the file contents.
// The buffer size can be adjusted as needed depending on
// the number and size of files being uploaded.
byte[] buffer = new byte[1024];
// Write the contents of the local file to the
// request stream.
using (Stream stream = request.GetRequestStream())
using (FileStream fsWorkbook = File.Open(LocalPath,
FileMode.Open, FileAccess.Read))
{
int i = fsWorkbook.Read(buffer, 0, buffer.Length);
while (i > 0)
{
stream.Write(buffer, 0, i);
i = fsWorkbook.Read(buffer, 0, buffer.Length);
}
}
// Make the PUT request.
response = request.GetResponse();
}
finally
{
response.Close();
}
}
The sample code describes a scenario for the 2007 versions of these products but other versions should behave in the same way.
What does the filename of a failed example looks like? Aren't documents used in SharePoint stored in the database? Or am I getting your problem wrong? Otherwise I could imagine that the file you are trying to store is write protected by the operation system and cannot be modified.