Restore primefaces documentViewer after page reload - jsf-2

I'm using documentViewer-component from primefaces-extensions like this:
<pe:documentViewer cache="true" value="#{myBean.streamedContent}"/>
myBean is SessionScoped.
If I reload the page the getter is called and the streamedContent is not empty, but the viewer shows an empty page and the message stream must have data.
How can I restore the document in the viewer on page reload?

I've reproduced your problem using PrimeFaces Extensions Showcase, so I used below code
ByteArrayOutputStream out = new ByteArrayOutputStream();
Document document = new Document();
PdfWriter.getInstance(document, out);
document.open();
for (int i = 0; i < 50; i++) {
document.add(new Paragraph("All work and no play makes Jack a dull boy"));
}
document.close();
content = new DefaultStreamedContent(new ByteArrayInputStream(out.toByteArray()), "application/pdf");
But I've found a solution. Problem exists only using DefaultStreamedContent but when I've checked the hierarchy I've found that there is also posibility to use ByteArrayContent. It works even after page reloading.
Sample usage:
content = new ByteArrayContent(out.toByteArray(), "application/pdf");

Related

Generating/Sending pdf email attachments within a hangfire job

In our ASP.NET MVC web application we send emails as part of scheduled tasks handled by Hangfire for which I am using Postal as described here
The method works fine and we are able to send HTML/text emails. Now we need to generate and attach PDF files as well. The attached PDF needs to be generated dynamically by use of a Razor template. First I tried to use Rotativa in order to generate the PDF. However I encountered the problem that method BuildPdf needs a ControllerContext which is not available in the background HangFire process. I tried to fake the ControllerContext as
using (var memWriter = new StringWriter(sb))
{
var fakeResponse = new HttpResponse(memWriter);
var fakeRequest = new HttpRequest(null, "http://wwww.oururl.com", null);
var fakeHttpContext = new HttpContext(fakeRequest, fakeResponse);
var emailController = new BackgroundEmailController();
var fakeControllerContext = new ControllerContext(new HttpContextWrapper(fakeHttpContext), new RouteData(), emailController);
var attachment = emailController.BillAttachment(email);
var pdf = attachment.BuildPdf(fakeControllerContext);
if (pdf != null && pdf.Count() > 0)
{
using (MemoryStream ms = new MemoryStream(pdf))
{
var contentType = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Application.Pdf);
email.Attach(new System.Net.Mail.Attachment(ms, contentType));
}
}
}
However this raised a NullReference error in Rotativa.
Then I tried first to compile the template view with RazorEngine to HTML(and then convert the HTML to pdf by some mean) as
var engineService = RazorEngineService.Create();
engineService.AddTemplate(cache_name, File.ReadAllText(billAttachmentTemplatePath));
engineService.Compile(cache_name, modelType: typeof(BillEmail));
var html = engineService.Run(cache_name, null, email);
using (var ms = CommonHelper.GenerateStreamFromString(html))
{
var contentType = new System.Net.Mime.ContentType(System.Net.Mime.MediaTypeNames.Text.Html);
email.Attach(new System.Net.Mail.Attachment(ms, contentType));
}
And it throws another NullReference in the RazorEngine dynamic DLL:
System.NullReferenceException: Object reference not set to an instance of an object.
at CompiledRazorTemplates.Dynamic.RazorEngine_bb2b366aaef64f2bbc2997353f88cc9e.Execute()
at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
I was wondering if anybody have suggestions for generating PDF files from a template in a Hangfire process?
If you are open to commercial solutions, you can try Telerik reporting and export it as pdf programmatically. You define your report and then invoke it to generate PDF on the server side, finally email the byte[] as email attachment. You can now kickoff this process using Hangfire job.
Here is a pseudo code assuming you have defined the structure of your report, Please look here for more details on how to create your report programatically.
public void GenerateAndEmailReport()
{
var reportSource = new InstanceReportSource();
Telerik.Reporting.Report report = new MyReport();
//populate data into report
reportSource.ReportDocument = report;
var reportProcessor = new ReportProcessor();
reportSource.ReportDocument = report;
var info = new Hashtable();
var result= reportProcessor.RenderReport("PDF", reportSource, info);
byte[]reportBytes = result.DocumentBytes;
SendEmail(reportBytes, "myreport.pdf"); // a method that takes the bytes and attach it to email.
}
Additional references from telerik.
send report as email
Generating PDF in console application
Saving a report programmatically

Download PDF file and show directly in browser in MVC Project

I am calling a Web API from MVC project. The Web API is returning PDF file that I want to show directly in the browser after clicking on a button. My problem is when I click on the link, it downloads the pdf file and shows the icon at the left bottom corner side and I have to click on it and to open the PDf in acrobat. How I can make it the way that by clicking the link it open the pdf directly in the browser?
This is my code in MVC project that open the pdf:
[HttpGet]
public FileResult openPdf(string name)
{
byte[] pdfByte = DownloadFile();
return File(pdfByte, "application/pdf", name);
}
internal byte[] DownloadFile()
{
string serverUrl = "http://localhost/GetPdf?Number=3671";
var client = new System.Net.WebClient();
client.Headers.Add("Content-Type", "application/pdf");
return client.DownloadData(serverUrl);
}
This is the method in my Web API that returns pdf:
public HttpResponseMessage GetPdfNameByRemRef(string RemoteRefNumber)
{
var stream = new MemoryStream();
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(stream.GetBuffer())
};
byte[] fileBytes = System.IO.File.ReadAllBytes(#"C:\Pdf\CreditApplication_08192006_102714AM_et montis.pdf");
response.Content = new ByteArrayContent(fileBytes);
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = customerInfo.Application_Filename;
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return response;
}
You can try to add Content-Disposition header with value inline.
Response.AddHeader("Content-Disposition", "inline;filename=fileName.pdf");
However, the behavior can differ on different browsers and on file types you are serving. If Content-Disposition is set to inline the browser will try to open the file within the browser, but it may fail if the file type is unknown (ex. .rar, .zip, .pdf /when pdf reader plugin is missing/, if the browser is old .. etc.).

iTextSharp creating file in memory resulting corrupted file

I am trying to create a pdf file in MVC using iTextSharp. I do have a following simple used case. File is getting created but when I open the PDF I am getting error file is corrupted unable to open the file. Any idea/help ?
My Controller code is a follows
public FileStreamResult GetPdfMemory()
{
iTextSharp.text.Document doc = new iTextSharp.text.Document();
MemoryStream mem = new MemoryStream();
PdfWriter pdfWriter = PdfWriter.GetInstance(doc, mem);
//pdfWriter.CloseStream = false;
doc.Open();
doc.Add(new Paragraph("Charts"));
mem.Position = 0;
FileStreamResult fileStreamResult = new FileStreamResult(mem, System.Net.Mime.MediaTypeNames.Application.Pdf)
{
FileDownloadName = "chart_" + ".PDF"
};
return fileStreamResult;
}
View :
#Html.ActionLink("Pdf Memory", "GetPdfMemory", "Home", null, new { id = "download"})
FYI : When I try to use FileStream instead of MemoryStream all works fine. But I need to create PDF using memorystream.
You manipulate (mem.Position = 0) and use (new FileStreamResult(mem, ...)) the MemoryStream before signalling iTextSharp that it can finalize the document. Thus, depending on whether implicit destruction of the Document and the PdfWriter or the use of the data in the memory stream comes first, you either have a PDF missing its closing parts or the closing parts (being written after you reposition the memory stream) overwriting the start of the data.
To signal to iTextSharp that it can finalize the document, please call doc.Close() before manipulating the memory stream or alternatively use the Document in a using block, e.g.:
using (MemoryStream ms = new MemoryStream()) {
// step 1
using (Document document = new Document()) {
// step 2
PdfWriter.GetInstance(document, ms);
// step 3
document.Open();
// step 4
document.Add(new Paragraph("HelloWorldMemory"));
}
HttpContext.Current.Response.BinaryWrite(ms.ToArray());
}
(shamelessly copied from the sample HelloWorldMemory.cs from the Webified iTextSharp Examples) for iText in Action — 2nd Edition)
Using using implicitly causes the Document to be closed.

Prevent ActionResult from posting to a new page?

My project is very similar to NerdDinner and I'm generating a pdf-document using PdfSharp.
In my view I'm using this:
<%: Html.ActionLink("Pdf", "GeneratePdf1", "Persons")%>
Calling this ActionResult:
public ActionResult GeneratePdf1()
{
// Create a new PDF document
PdfDocument document = new PdfDocument();
document.Info.Title = "Created with PDFsharp";
// Create an empty page
PdfPage page = document.AddPage();
// Get an XGraphics object for drawing
XGraphics gfx = XGraphics.FromPdfPage(page);
// Create a font
XFont font = new XFont("Verdana", 20, XFontStyle.BoldItalic);
// Draw the text
gfx.DrawString("Hello, World!", font, XBrushes.Black,
new XRect(0, 0, page.Width, page.Height),
XStringFormats.Center);
// Save the document...
const string filename = "HelloWorld.pdf";
document.Save(filename);
Process.Start(filename);
return View();
}
A few questions/problems on this:
I don't want the link to post. When you click the link it should just open the file, but I don't know what to return to prevent it from posting.
I'd also like the "save/open" dialog to appear. Right now the pdf file is displayed directly.
You want to return a FileResult not an ActionResult. Also, I would save it to a MemoryStream and return the byte array, not use a file. Full solution below.
public FileResult GeneratePdf1()
{
// Create a new PDF document
PdfDocument document = new PdfDocument();
document.Info.Title = "Created with PDFsharp";
// Create an empty page
PdfPage page = document.AddPage();
// Get an XGraphics object for drawing
XGraphics gfx = XGraphics.FromPdfPage(page);
// Create a font
XFont font = new XFont("Verdana", 20, XFontStyle.BoldItalic);
// Draw the text
gfx.DrawString("Hello, World!", font, XBrushes.Black,
new XRect(0, 0, page.Width, page.Height),
XStringFormats.Center);
MemoryStream stream = new MemoryStream();
document.Save(stream, false);
return File(stream.ToArray(), "application/pdf");
}
you should replace these lines:
Process.Start(filename);
return View();
with
return File(filename, "application/pdf");
You may also have a third param with the downloaded filename, if it should be different to the action name.
return File(filename, "application/pdf", "myGeneratedPdf.pdf");
Also be sure the filename is unique per request or use a MemoryStream like it is suggested by Chris Kooken.
Btw: Process.Start(filename) will run the file on the server machine. that may work on your development machine, but on a live server the pdf will open on the server!!!

ASP.NET MVC FileStreamResult not working as intended

I have the following code which I stripped out of any non-essential lines to leave the minimun reproducable case. What I expect is for it to return the image, but it doesn't. As far as I can see it returns an empty file:
public ActionResult Thumbnail(int id) {
var question = GetQuestion(db, id);
var image = new Bitmap(question.ImageFullPath);
MemoryStream stream = new MemoryStream();
image.Save(stream, ImageFormat.Jpeg);
return new FileStreamResult(stream, "image/jpeg");
}
Can you identify what's wrong with this code? In the debugger I can see that the stream grows in size so it seems to be getting the data although I haven't been able to verify it's the correct data. I have no idea how to debug the FileStreamResult itself.
You need to insert
stream.Seek(0, SeekOrigin.Begin);
after the call to
Image.Save()
This will rewind the stream to the beginning of the saved image. Otherwise the stream will be positioned at the end of the stream and nothing is sent to the receiver.
Try rewinding the MemoryStream. The "cursor" is left at the end of the file and there is nothing to read until you "rewind" the stream to the beginning.
image.Save( stream, ImageFormat.Jpeg );
stream.Seek( 0, SeekOrigin.Begin );
return new FileStreamResult( stream, "image/jpeg" );

Resources