I'm working on MVC5 project on .NET 4.5.3. I have View with #Html.BeginForm and FormMethod.Post this view calls [HttpPost] ActionResult. In the Controller i get necessary ID's from the submitted form and then pass them for exporting.
[HttpPost]
public ActionResult PrepareForExport()
{
ExportService export = new ExportService();
if (Request.Form["button"] != null)
{
string selected = Request.Form["button"].ToString();
export.GeneratePdf(ptoRequestsService.GetByID(Convert.ToInt32(selected)));
}
else if (Request.Form["toExport"] != null)
{
List<PtoRequest> ptoListForExport = new List<PtoRequest>();
string selectedItems = Request.Form["toExport"].ToString();
string[] selectedList = selectedItems.Split(',');
foreach (var pto in selectedList)
{
ptoListForExport.Add(ptoRequestsService.GetByID(Convert.ToInt32(pto)));
}
export.GenerateZip(ptoListForExport);
}
return RedirectToAction("Requests" + LoggedUser.ID);
}
And in the ExportService class i have this export method.
public void GenerateZip(List<PtoRequest> approvedPtos)
{
byte[] pdfContent = null;
string dateFormat = "yyyy-MM-dd";
string filePath = null;
if (!Directory.Exists(HttpContext.Current.Server.MapPath(#"~/Files/PdfFiles/")))
{
Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/Files/PdfFiles/"));
filePath = HttpContext.Current.Server.MapPath("~/Files/PdfFiles/");
}
else
{
filePath = HttpContext.Current.Server.MapPath("~/Files/PdfFiles/");
}
foreach (var Pto in approvedPtos)
{
pdfContent = FillPdfTemplate(Pto);
string fileName = Pto.User.FirstName + " " + Pto.User.LastName + "_" + Pto.StartDate.ToString(dateFormat) + ".pdf";
string fileDirectory = filePath + fileName;
using (FileStream fs = new FileStream(fileDirectory, FileMode.OpenOrCreate))
{
fs.Write(pdfContent, 0, pdfContent.Length);
}
}
string zipName = String.Format("Report_{0}.zip", DateTime.Now.ToString("yyyy-mm-dd-HHmmss"));
string zipFile = filePath + zipName;
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(filePath);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.BufferOutput = false;
HttpContext.Current.Response.ContentType = "application/zip";
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + zipName);
zip.Save(HttpContext.Current.Response.OutputStream);
HttpContext.Current.Response.Write(zip.ToString());
HttpContext.Current.Response.End();
Directory.Delete(filePath, true);
}
}
Everything works fine, but when my method complete the job i got exception with code 500. I do my googling to understand my problem and it is something like this 1. I understand that the problem is in HttpHeader but did not understand how to solve it in my case. Then i tried the solution with if (!Response.IsRequestBeingRedirected) but i still got this exception. After this I tried to return ZipFile from GenerateZip Method and instead calling Response in ExportService class to call it at Controller, but i still got this Error. I have an idea to remove [HttpPost] attribute and to take my ID's in other way but my point is to do it that way. Can anyone point me in any direction to solving this? What is the right decision, where should i call Response for this case, is it option to write jQuery script to prevent submitting form in .cshtml?
You are calling HttpContext.Current.Response.End(); in your GenerateZip method and then attempting to Redirect the user in the ControllerAction. This is causing your "Headers cannot be written after response is sent" error. Instead of that, return a byte array or Stream object (FileStream?) from the GenerateZip method and return a FileResult in the Action. Redirecting the user after the file download can (only?) be achieved using JavaScript. Here is an example. How to return a FileResult AND RedirectToAction in ASP.NET MVC
Related
I know this question was asked & answered before, but the solutions don't work for me. I have to dynamically create a list of PDFs, and each row has a checkbox. You check the PDFs you want to download, whose ID get passed to a function to create the PDF. I store those PDF's in a list, which gets passed to the Zip function (DotNetZip). Problem is, when first generating a PDF, I get that error somewhere in the middle of creating it, about halfway through, when adding a new page in the PDF. Usually it's the same spot but occasionally it changes where it crashes. Could anyone look at my code and point out where I'm messing up?
protected void Download_PDF_Click(object sender, EventArgs e)
{
Response.Clear();
Response.BufferOutput = false;
HttpContext c = HttpContext.Current;
String archiveName = "Arhiva Inspectii.zip";
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=\"" + archiveName + "\"");
int nr_rows = tbl_inspectii.Rows.Count - 1;
foreach (String id_chbx in ID_Checkbox)
{
CheckBox chbx = tbl_inspectii.FindControl(id_chbx) as CheckBox;
String PDF_id = "";
if(chbx != null)
{
if(chbx.Checked)
{
PDF_id = chbx.ID.Replace("ChBx", string.Empty);
Create_PDF(PDF_id);
}
}
}
string destdir = Server.MapPath("~/Zip/") + archiveName;
using (ZipFile zip = new ZipFile())
{
zip.AddFiles(PDF_list, destdir);
zip.Save(destdir);
}
}
protected byte[] Create_PDF(String id_insp_max)
{
using (MemoryStream ms = new MemoryStream())
{
Document brosura = new Document(PageSize.A4);
brosura.SetMargins(40, 40, 40, 40);
PdfWriter wri = PdfWriter.GetInstance(brosura, ms);//new FileStream(Server.MapPath("Pdf/") + titlu_pdf + ".pdf", FileMode.Create)
HttpContext.Current.Response.AppendHeader("Content-Disposition", "inline; filename=\"" + titlu_pdf + ".pdf\"");
PdfWriter.GetInstance(brosura, HttpContext.Current.Response.OutputStream);
brosura.Open();
//lots of SQL, and brosura.Add();
//at some point, a brosura.Add() has the error halfway through the pdf
brosura.Close();
PDF_list.Add(titlu_pdf);
wri.Close();
return ms.ToArray();
}
}
Try to move the Response block after you closed the Document.
And try to add Response.End();
And why are you return the Array in Create_PDF but does not using it?
I have this code, it checks if a photo exists and if it has been created within the past 14 days. The issue I am finding is that the code is passing the System.IO.File.Exists() but then when I go to return the file at the end, it errors with a "Could not find file". Anyone have any ideas? I am suspecting it is the way I am using Path.Combine?
EDIT
Managed to get it working by returning a filestream rather than a path.
var img = System.IO.File.OpenRead(PhotoPathAndFileName);
return File(img, "image/jpeg");
**
public IActionResult GetPhoto(string ID, int MaxHeight, int MaxWidth, bool isStudentImage = true)
{
var PhotoFileName = ID + "_" + MaxHeight.ToString() + "x" + MaxWidth.ToString() + ".jpg";
var PhotoPathAndFileName = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot\\images\\App_Data\\_cacheIDPhoto", PhotoFileName);
var FileExists = false;
if (System.IO.File.Exists(PhotoPathAndFileName))
{
var CreateDate = System.IO.File.GetCreationTime(PhotoPathAndFileName);
if (CreateDate < DateTime.Now.AddDays(-14))
{
System.IO.File.Delete(PhotoPathAndFileName);
}
else
{
FileExists = true;
}
}
if (!FileExists)
{
PhotoPathAndFileName = CreatePhotoFile(Convert.ToInt32(ID), PhotoPathAndFileName, MaxWidth, MaxHeight, isStudentImage);
}
return File(PhotoPathAndFileName, "image/jpeg");
}
Managed to get it working by returning a filestream rather than a path.
var img = System.IO.File.OpenRead(PhotoPathAndFileName);
return File(img, "image/jpeg");
I am using Microsoft.Report.Viewer for generate PDF in my ASP .NET WEB API application. So far when my report get the data directly from database, there is no problem. The problem is, when I try to create a report with subreport within and the subreport data is got from code in a list form, I didn't get the expected result. What I do is create a report with subreport from this tutorial. But when I generate the PDF, what I get is this message .
Here is my code :
My Web API to generate the PDF :
[Route("GeneratePDFForDailyProgram")]
[HttpGet]
[AllowAnonymous]
public async Task<IHttpActionResult> GeneratePDFForDailyProgram(int id) {
string guid = Guid.NewGuid().ToString();
string fileName = string.Concat("Booking_No" + Convert.ToString(id) + "_" + guid.ToUpper() + ".pdf");
string filePath = HttpContext.Current.Server.MapPath("~/Content/Temp/" + fileName);
string name = Request.RequestUri.GetLeftPart(UriPartial.Authority) + System.Configuration.ConfigurationManager.ConnectionStrings[System.Configuration.ConfigurationManager.AppSettings["CUSTOMPORT"]];
string name2 = Request.GetRequestContext().VirtualPathRoot;
List<TourDestination> destination = new List<TourDestination>();
TourTransactionSummaryViewModel summaryViewModel = new TourTransactionSummaryViewModel();
try {
//summaryViewModel = await CalcSummaryViewModel(transaction, summaryViewModel, db);
summaryViewModel.DailyPrograms = DailyProgram(id);
destination = TourDestination(summaryViewModel.DailyPrograms);
List < Movement > movementList = summaryViewModel.DailyPrograms.FirstOrDefault().Movements.ToList();
Reports.ReportGenerator.GeneratePDFForDailyProgram(summaryViewModel.DailyPrograms, destination, filePath);
//await Reports.ReportGenerator.GeneratePDFForMovement(movementList, filePath);
HttpResponseMessage result = null;
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(new FileStream(filePath, FileMode.Open));
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
result.Content.Headers.ContentDisposition.FileName = fileName;
result.Dispose();
string convertedFilePath = filePath.Replace(#"\\", #"\");
} catch (Exception ex) {
return BadRequest(ex.Message);
}
return Ok(name + name2 + "/Content/Temp/" + fileName);
}
As you can see from the code above, I use GeneratePDFForDailyProgram method to generate the PDF.
Here my GeneratePDFForDailyProgram method :
public static bool GeneratePDFForDailyProgram(TourTransactionSummaryViewModel.DailyProgram[] dailyPrograms, List<TourDestination> destination , string filePath) {
try {
string binPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "bin");
var assembly = Assembly.Load(System.IO.File.ReadAllBytes(binPath + "\\TripPlannerAPI.dll"));
using (Stream stream = assembly.GetManifestResourceStream(DailyProgramReport)) {
var viewer = new ReportViewer();
viewer.LocalReport.EnableExternalImages = true;
viewer.LocalReport.LoadReportDefinition(stream);
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string filenameExtension;
viewer.LocalReport.SubreportProcessing += new Microsoft.Reporting.WebForms.SubreportProcessingEventHandler(LocalReport_SubreportProcessing);
viewer.LocalReport.DataSources.Add(new ReportDataSource("DailyProgram", dailyPrograms));
byte[] bytes = viewer.LocalReport.Render(
"PDF", null, out mimeType, out encoding, out filenameExtension,
out streamids, out warnings);
using (FileStream fs = new FileStream(filePath, FileMode.Create)) {
fs.Write(bytes, 0, bytes.Length);
fs.Flush();
}
stream.Flush();
}
} catch (Exception ex) {
return false;
}
return true;
}
To run the SubReport, I use the eventhandler code :
private static void LocalReport_SubreportProcessing(object sender, SubreportProcessingEventArgs e) {
DateTime movementDate = Convert.ToDateTime(e.Parameters[0].Values[0]);
TourTransactionsController controller = new TourTransactionsController();
var movement = controller.Movements();
List<Movement> movementList = new List<Movement>();
movementList.Add(new Movement {
Destination = "TEST",
MovementDescription = "TEST",
DateTime = Convert.ToDateTime("2017-09-25") //HARDCODE FOR TEST
});
e.DataSources.Clear();
e.DataSources.Add(new Microsoft.Reporting.WebForms.ReportDataSource() {
Name = "DSMovements",
Value = movementList
});
//throw new NotImplementedException();
}
What I did try :
Check the sub report name, and change it from report name to report file url. Still get the same message.
Generate the Sub Report directly, not from the main report. The subreport is successfully generated.
PS : When I debug my code, and put a breakpoint in my event handler LocalReport_SubreportProcessing the breakpoint is not hitting while debugging
My Questions are :
Why my breakpoint on the eventhandler is not hitting while debugging ?
Is it possible that eventhandler which is not hitting while debugging, could be the reason I got the message The subreport 'MovementReport' could not be
found at the specified location MovementReport.
Please verify that the subreport has been
published and that the name is correct. ?
Is there any other way for me to create a main report with data from db, and the subreport with data from code in list form ?
Any help is appreciated.
I have some code that I am using to open a document on my MVC site. The code seems to work fine, and the document is opening, however an error is occurring in the background saying "Cannot redirect after HTTP headers have been sent". The code i'm using is below, essentially what i'm doing is submitting the page with a certain command being passed through as a variable, and then redirecting back to the same page:
public ActionResult List(string command, string document, int id = 0)
{
if (id > 0)
{
ViewData["id"] = id;
}
if (command == "documentOpen")
{
DocumentOpen(document);
return RedirectToAction("Edit", "Fault", new { id });
}
else if (command == "documentDelete")
{
DocumentDelete(document);
return RedirectToAction("Edit", "Fault", new { id });
}
else
{
IEnumerable<Fault.Document> results = _DocumentRepository.GetDocumentList(Convert.ToInt32(ViewData["id"]));
return PartialView("List", results);
}
}
private void DocumentOpen(string document)
{
var fileinfo = new FileInfo(Server.MapPath(Path.Combine("~/", document)));
Response.Clear();
Response.AddHeader("Content-Disposition", "filename=" + fileinfo.Name);
Response.AddHeader("Content-Length", fileinfo.Length.ToString());
Response.ContentType = "application/octet-stream";
Response.WriteFile(fileinfo.FullName);
Response.Flush();
}
private void DocumentDelete(string documentId)
{
//Read document from the server
var document = _ITFaultDocumentRepository.GetFaultDocument(Convert.ToInt32(documentId), null);
//Delete the physical file
System.IO.File.Delete(Server.MapPath(Path.Combine("../", document.doc_name)));
//Delete record in database
_DocumentRepository.DeleteFaultDocument(document.doc_id);
}
I've done some looking around and I think that the error is occuring because i'm redirecting the page after opening the document, but I haven't found a way to do it other than this yet. If possible i'd like to have the document open in a new tab/window, is something like this possible in C#?
Thanks
public ActionResult GeneratePdf(int id)
{
var labelRepository = new LabelRepository();
var label = labelRepository.GetLabel(id);
if (String.IsNullOrEmpty(label.PDFLocation))
{
var action = Url.Content("~/Label/ViewPdf/" + id); //doc
Doc theDoc = new Doc();
string filename = Guid.NewGuid().ToString();
string path = Server.MapPath("~/" + filename + ".pdf");
theDoc.AddImageUrl("action");
theDoc.Save(path);
theDoc.Clear();
label.PDFLocation = path;
labelRepository.Save();
return base.File(path, "application/pdf");
}
else
{
return base.File(label.PDFLocation, "application/pdf");
}
}
This won't add my image url so my pdf won't open up so I can see it. Any ideas?-
In the example here, a complete URL is being passed to the AddImageUrl() function, not a fragment of an URL, as your example shows.
Perhaps you need a call to RouteUrl() so that you can get a complete Url to pass to your AddImageUrl() method?