ASP.Net MVC 3
I have an Action that returns a FileStreamResult after it imports a PDF document and stamps it with a watermark. Since it is possible to have a file not found error, how do I return a view instead of a filestream?
To complicate things I am using Philip Hutchison's jQuery PDFObject (http://pdfobject.com) to call the action and render it in a DIV so I cannot redirect on the server side.
To reiterate: It's a jQuery link on the page that fills a DIV with the results from a PDF filestream. The only 'hack' thing that I can think of is to send an Error.pdf file.
Your thoughts?
I ended up creating an error PDF memory stream.
MemoryStream ms = new MemoryStream();
try
{
PdfReader reader = new PdfReader(readerURL);
StampWatermark(WO, ms, reader);
}
catch (Exception ex)
{
RenderErrorPDF(WO, ms, ex);
}
byte[] byteinfo = ms.ToArray();
ms.Write(byteinfo, 0, byteinfo.Length);
ms.Position = 0;
ms.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(ms, "application/pdf");
RenderErrorPDF method
private static void RenderErrorPDF(WorkOrder WO, MemoryStream ms, Exception ex)
{
BaseFont bfTimes = BaseFont.CreateFont(BaseFont.HELVETICA_BOLD, BaseFont.CP1250, false);
var doc = new Document(new Rectangle(792f, 540f));
var wr = PdfWriter.GetInstance(doc, ms);
doc.Open();
doc.Add(new Paragraph("There was an error rendering the file that you requested...", new Font(bfTimes, 24f)));
doc.Add(new Paragraph(string.Format("\r\rFile: {0}", WO.DRAWING_FILE)));
doc.Add(new Paragraph(string.Format("\r\rError: {0}", ex.Message)));
wr.CloseStream = false;
doc.Close();
}
Related
I create a simple code for generate a pdf file, this is the code
public IActionResult reporte()
{
Document doc=new Document();
MemoryStream ms = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(doc, ms);
doc.AddAuthor("Jose");
doc.AddTitle("Example");
doc.Open();
doc.Add(new Phrase("PDF example"));
writer.Close();
doc.Close();
ms.Seek(0, SeekOrigin.Begin);
var nameFile = string.Concat("reportName", ".pdf");
return File(ms,"application/pdf",nameFile);
}
The PDF file can be opened with web explorer, it's ok but when I save the file and try to open with Adobe Acrobat Reader, show an error dialog.
I need to add some instruction for compatibility?
I am using itextsharp to generate PDF in MVC application and this works very well on dev machine. When i deploy this on pre-production machine, this generates blank PDF page and no exception. I tried quick debugging using logging to see if I'm getting content or not after line StringReader sr = new StringReader(sb.ToString()); and i see it has content.
After some research I found one faced similar issue and he resolved this issue just by writing document.Open() after PdfWriter.GetInstance. Source: "The document is not open" error only in production with iTextSharp
After that, I thought to use code which worked for many developers How to convert HTML to PDF using iTextSharp and I still see blank PDF on pre-production but works on dev machine.
I doubt if itextsharp has some sort of dependency on operation system or office suit (with itextsharp latest updates) because as I said it works on dev machine.
Here's the code which generates blank PDF on production machine Windows Server 2012 R2.
public ActionResult DownloadOrderReceipt(string id)
{
string companyName = "Name Here";
int orderNo = 2303;
DataTable dt = new DataTable();
dt.Columns.AddRange(new DataColumn[5]
{
new DataColumn("ProductId", typeof(string)),
new DataColumn("Product", typeof(string)),
new DataColumn("Price", typeof(int)),
new DataColumn("Quantity", typeof(int)),
new DataColumn("Total", typeof(int))
});
dt.Rows.Add(101, "Sun Glasses", 200, 5, 1000);
dt.Rows.Add(102, "Jeans", 400, 2, 800);
dt.Rows.Add(103, "Trousers", 300, 3, 900);
dt.Rows.Add(104, "Shirts", 550, 2, 1100);
using (StringWriter sw = new StringWriter())
{
using (HtmlTextWriter hw = new HtmlTextWriter(sw))
{
StringBuilder sb = new StringBuilder();
// generate invoice header
sb.Append("<table width='100%' cellspacing='0' cellpadding='2'>");
sb.Append("<tr><td align='center' style='background-color: #18B5F0' colspan = '2'><b>Order Sheet</b></td></tr>");
sb.Append("<tr><td colspan = '2'></td></tr>");
sb.Append("<tr><td><b>Order No: </b>");
sb.Append(orderNo);
sb.Append("</td><td align = 'right'><b>Date: </b>");
sb.Append(DateTime.Now);
sb.Append(" </td></tr>");
sb.Append("<tr><td colspan = '2'><b>Company Name: </b>");
sb.Append(companyName);
sb.Append("</td></tr>");
sb.Append("</table>");
sb.Append("<br />");
// generate invoice items grid
sb.Append("<table border = '1'>");
sb.Append("<tr>");
foreach (DataColumn column in dt.Columns)
{
sb.Append("<th style = 'background-color: #D20B0C;color:#ffffff'>");
sb.Append(column.ColumnName);
sb.Append("</th>");
}
sb.Append("</tr>");
foreach (DataRow row in dt.Rows)
{
sb.Append("<tr>");
foreach (DataColumn column in dt.Columns)
{
sb.Append("<td>");
sb.Append(row[column]);
sb.Append("</td>");
}
sb.Append("</tr>");
}
sb.Append("<tr><td align = 'right' colspan = '");
sb.Append(dt.Columns.Count - 1);
sb.Append("'>Total</td>");
sb.Append("<td>");
sb.Append(dt.Compute("sum(Total)", ""));
sb.Append("</td>");
sb.Append("</tr></table>");
// export html string into PDF
StringReader sr = new StringReader(sb.ToString());
Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 10f, 0f);
HTMLWorker htmlparser = new HTMLWorker(pdfDoc);
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, Response.OutputStream);
pdfDoc.Open();
htmlparser.Parse(sr);
pdfDoc.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=Invoice_" + orderNo + ".pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Write(pdfDoc);
Response.End();
}
}
return new EmptyResult();
}
Any help on this is highly appreciated.
Updated 1 (still same issue)
I just updated and simplified my code according to suggestions, but I still see same issue:
public ActionResult DownloadOrderReceipt(string id)
{
var order = orderRepository.Get(id);
if (order == null)
return new EmptyResult();
using (MemoryStream stream = new MemoryStream())
{
string htmlContent = "<p>Write this on PDF Page</p>";
StringReader sr = new StringReader(htmlContent);
Document pdfDoc = new Document(PageSize.A4, 10f, 10f, 100f, 0f);
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, stream);
pdfDoc.Open();
XMLWorkerHelper.GetInstance().ParseXHtml(writer, pdfDoc, sr);
pdfDoc.Close();
return File(stream.ToArray(), "application/pdf", "Invoice_121212.pdf");
}
}
This also works on dev machine but on pre-production this generates blank PDF page.
Updated 2 (still same issue)
My attempt to fix this working on pre-production is ON. Here's the another two samples which I just tried and no help.
public ActionResult DownloadOrderReceipt(string id)
{
byte[] bytes;
using (var ms = new MemoryStream())
{
using (var doc = new Document())
{
using (var writer = PdfWriter.GetInstance(doc, ms))
{
doc.Open();
var example_html = #"<p>This <em>is </em><span class=""headline"" style=""text-decoration: underline;"">some</span> <strong>sample <em> text</em></strong><span style=""color: red;"">!</span></p>";
var example_css = #".headline{font-size:200%}";
// example 1
//using (var srHtml = new StringReader(example_html))
//{
// XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, srHtml);
//}
// example 2
using (var msCss = new MemoryStream(Encoding.UTF8.GetBytes(example_css)))
{
using (var msHtml = new MemoryStream(Encoding.UTF8.GetBytes(example_html)))
{
XMLWorkerHelper.GetInstance().ParseXHtml(writer, doc, msHtml, msCss);
}
}
doc.Close();
}
}
bytes = ms.ToArray();
}
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=Invoice_1212121212.pdf");
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.BinaryWrite(bytes);
Response.End();
return new EmptyResult();
}
SOLUTION
Updating this question after long time, because i noticed new comments.
In the process of code review I found that developer had wrote logic to minify HTML content dynamically that caused the bites removed from PDF too and hence empty PDF.
This works for me, MemoryStream is important:
public class PDFController : Controller
{
// GET: PDF
public ActionResult Index(string address)
{
SelectPdf.HtmlToPdf converter = new SelectPdf.HtmlToPdf();
SelectPdf.PdfDocument doc = converter.ConvertUrl(address);
var bytes = doc.Save();
MemoryStream mstream = new MemoryStream(bytes);
doc.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=kundenvereinbarung.pdf");
Response.Buffer = true;
mstream.WriteTo(Response.OutputStream);
Response.End();
return null;
}
}
I am trying to create a table containing audio files, which should start playing when the user clicks the play button. This is what I've tried so far:
Controller:
[HttpGet]
public ActionResult PlayFile(string FilePath)
{
WebClient WC = new WebClient();
WC.Credentials = new System.Net.NetworkCredential("username", "password");
byte[] buff = WC.DownloadData(FilePath);
var SplitFileName = FilePath.Split('\\');
string FileName = "Recording_" + SplitFileName[SplitFileName.Count() - 1];
Response.AddHeader("Content-Disposition", "inline; filename=" + FileName);
MemoryStream stream = new MemoryStream(buff);
return new FileStreamResult(stream, "audio/wav");
//I have also tried:
//return File(buff, "audio/wav");
}
The audio tags look like this:
<td>
<audio controls preload='none'>
<source src='/Components/PlayFile?FilePath=[filename.wav]' type='audio/wav'>
</audio>
</td>
When running the site locally in Chrome, all the files have the length 0:00, and you can click the play button once but the file is not played. After the play button has been clicked once it is not possible to click it again. It is however possible to download the file
and play it. When running the site locally in Firefox, the files also have the length 0:00, and when you click the play button the control disappears. It is also possible to download the file in Firefox. Does anyone know what could be causing this?
The problem was that the audio files were in GSM format and needed to be converted to PCM. This code works:
[HttpGet]
public ActionResult PlayFile(string FilePath)
{
WebClient WC = new WebClient();
WC.Credentials = new System.Net.NetworkCredential("username", "password");
byte[] buff = WC.DownloadData(FilePath);
var SplitFileName = FilePath.Split('\\');
string FileName = "Recording_" + SplitFileName[SplitFileName.Count() - 1];
MemoryStream ms = new MemoryStream();
ms.Write(buff, 0, buff.Length);
ms.Seek(0, SeekOrigin.Begin);
MemoryStream outputStream = new MemoryStream();
using (NAudio.Wave.WaveFileReader reader = new WaveFileReader(ms))
using (NAudio.Wave.WaveStream waveStream = NAudio.Wave.WaveFormatConversionStream.CreatePcmStream(reader))
using (NAudio.Wave.WaveFileWriter waveFileWriter = new WaveFileWriter(outputStream, waveStream.WaveFormat))
{
byte[] bytes = new byte[waveStream.Length];
waveStream.Position = 0;
waveStream.Read(bytes, 0, (int)waveStream.Length);
waveFileWriter.Write(bytes, 0, bytes.Length);
waveFileWriter.Flush();
}
return File(outputStream.ToArray(), "audio/wav");
}
Can you try with below as its working for me.
public FileResult PlayFile(string FilePath)
{
return new FilePathResult(FilePath, "audio/wav");
}
Also, try changing "audio/wav" to "audio/mp3", if it helps.
I am new to this library but I couldn't find anything about downloading to the filestream with this library, I could only find a document.Save(filepath) option which is not always allowed.
I would like to create a filestream so the file gets downloaded to the directed Downloads folder.
Could someone point me in the right direct?
There is a Save overload that takes a MemoryStream object instead of path to a file.
The PDFSharp website has an example showing how to do this:
private void Page_Load(object sender, System.EventArgs e)
{
// Create new PDF document
PdfDocument document = new PdfDocument();
this.time = document.Info.CreationDate;
document.Info.Title = "PDFsharp Clock Demo";
document.Info.Author = "Stefan Lange";
document.Info.Subject = "Server time: " +
this.time.ToString("F", CultureInfo.InvariantCulture);
// Create new page
PdfPage page = document.AddPage();
page.Width = XUnit.FromMillimeter(200);
page.Height = XUnit.FromMillimeter(200);
// Create graphics object and draw clock
XGraphics gfx = XGraphics.FromPdfPage(page);
RenderClock(gfx);
// Send PDF to browser
MemoryStream stream = new MemoryStream();
document.Save(stream, false);
Response.Clear();
Response.ContentType = "application/pdf";
Response.AddHeader("content-length", stream.Length.ToString());
Response.BinaryWrite(stream.ToArray());
Response.Flush();
stream.Close();
Response.End();
}
Using Open XML SDK, the following gives "Memory stream is not expandable" when I reach the line FeedData(msData):
// Bytes in, bytes out
internal static byte[] UpdateDataStoreInMemoryStream(byte[] bytes,
XmlDocument xdocData)
{
using (var msDoc = new MemoryStream(bytes))
{
using (WordprocessingDocument wd = WordprocessingDocument.Open(msDoc, true))
{
MainDocumentPart mdp = wd.MainDocumentPart;
CustomXmlPart cxp = mdp.CustomXmlParts.SingleOrDefault<CustomXmlPart>();
using (MemoryStream msData = new MemoryStream())
{
xdocData.Save(msData);
msData.Position = 0;
// Replace content of ...\customXml\item1.xml.
cxp.FeedData(msData);
// "Memory stream is not expandable" if more data than was there initially.
}
}
return msDoc.ToArray();
}
}
Note: it is not msData that is the trouble but msDoc.
Stein-Tore
The trouble was (actually quite obvious from the error message) that
using (var msDoc = new MemoryStream(bytes)) ...
creates a fixed size MemoryStream.
So solution is to create an expandable MemoryStream:
MemoryStream msDoc = new MemoryStream();
msDoc.Write(bytes, 0, bytes.Length);
msDoc.Position = 0;
...