How do I print an existing PDF from a code-behind page? - c#

I've seen a few questions like this around SO but couldn't find anything that's right for me. The chain of events that I would like to occur is as follows:
User clicks an ASP.NET button control
This fires that button's onclick event, which is a function foo() in the C# codebehind
foo() calls some other (unimportant) function which creates a PDF which ends up saved to the server's disk. That function returns the path to the PDF
Without any other user interaction, once the PDF is generated, the print dialog box opens in the user's browser to print that PDF
What do I need to do to accomplish step 4? Ideally, it would be something I can call in foo(), passing in the path to the PDF, that will trigger the print dialog box in the user's browser (printing the PDF and not the page the from which the onclick fired).
I think that I might be able to forward to the URL of the PDF document, and embed some Javascript in the PDF that automatically prints it, but I would rather not - I don't necessarily want to print the PDF every time it's opened (in a browser). Any other good way to do this?

Response.Clear()
Response.AddHeader("Content-Disposition", "attachment; filename=myfilename.pdf")
Response.ContentType = "application/pdf"
Response.BinaryWrite(ms.ToArray())
Where ms = a memorystream containing your file (you don't have to write it to disk in-between.)
Otherwise if you absolutely have to deal with coming from the hard disk, use:
Response.WriteFile("c:\pathtofile.pdf")

Thanks for the answer Vdex. Here is the iText/C# version.
PdfAction action = new PdfAction();
action.Put(new PdfName("Type"), new PdfName("Action"));
action.Put(new PdfName("S"), new PdfName("Named"));
action.Put(new PdfName("N"), new PdfName("Print"));
PdfReader reader = new PdfReader(ReportFile.FullFilePath(reportFile.FilePath));
PdfStamper stamper = new PdfStamper(reader, Response.OutputStream);
stamper.Writer.CloseStream = false;
stamper.Writer.SetOpenAction(action);
stamper.Close();

I did not try this, but it is an idea:
If you can read querystring parameters in javascript embedded in the PDF, then embed the javascript, and make the print function be conditional on a javascript parameter.
That way, if you redirect to, for instance, yourpdf.pdf?print, it will print, and if it is opened without the print parameter, it behaves as any other normal PDF would do.

If you want to go down the path of printing it as soon as it is opened:
There is a flag that you can insert into the PDF that forces it to open up the print dialog as soon as the PDF is opened. I did this ages ago using the abcpdf component in classic ASP, the code looked something like:
Set oDoc = Server.CreateObject("ABCpdf4.Doc")
oDoc.SetInfo oDoc.Root, "/OpenAction", "<< /Type /Action /S /Named /N /Print >>"
Obviously the code would look different depending on which PDF creation tool you are using...

The solution I arrived at was this:
Create a new ASP.NET web form (I called mine BinaryData.aspx) to serve as a placeholder for the PDF. In the code behind, the only method should be Page_Load, which looks like:
protected void Page_Load(object sender, System.EventArgs e)
{
//Set the appropriate ContentType.
Response.ContentType = "Application/pdf";
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Cache-Control", "no-cache");
//Get the physical path to the file.
string FilePath = (string)Session["fileLocation"];
if ( FilePath != null )
{
string FileName = Path.GetFileName(FilePath);
Response.AppendHeader("Content-Disposition", "attachment; filename="+FileName);
//Write the file directly to the HTTP content output stream.
Response.WriteFile(FilePath);
Response.End();
}
}
The PDF is passed in to the page through the Session variable named "fileLocation". So, all I have to is set that variable, and then call Response.Redirect("BinaryData.aspx").
It doesn't automatically print, but it triggers the download of the PDF without leaving the current page (which is good enough for me).

Related

ASP.NET Meta refresh Force Download

I am using a meta refresh tag to point to a file that i want to download from a server. How do i make it so that it downloads all files, rather than opens them in the browser. I am adding the meta tag in code behind like this:
String filename = filenode.Element("name").Value.ToString();
HtmlMeta redirectMetaTag = new HtmlMeta();
redirectMetaTag.HttpEquiv = "Refresh";
redirectMetaTag.Content = string.Format("2;url=http://example.example.net/example/" + filename);
this.Header.Controls.Add(redirectMetaTag);
At the moment, thie file eg a jpg is just being opened in the browser
If you want to force the browser to open a Save As dialog you must add a couple of custom headers for the page.
I sugggest you to use the TransmitFile function:
Response.ContentType = "image/jpeg";
Response.AppendHeader("Content-Disposition","attachment; filename=[your-file-name]");
Response.TransmitFile( Server.MapPath("~/images/[your-file-name]") );
Response.End();
It will automatically write the file content to the OutputStream of the Response.

Stream PDF to browser?

Could anyone please tell me how could I stream a pdf to a new tab browser? I just have the pdf stream on memory and I want when I click over the link to show the PDF in a new tab or window browser.
How could I do that?
Thank!!!
I have this link:
<a id="hrefPdf" runat="server" href="#" target="_blank"><asp:Literal ID="PdfName" runat="server"></asp:Literal></a>
In my code behind I have this on the onload event:
Stream pdf= getPdf
if (pdf != null)
{
SetLinkPDF(pdf);
}
private void SetLinkPDF(IFile pdf)
{
hrefPdf.href = "MyPDF to the Browser"
PdfName.Text = pdf.PdfName;
}
Someway I have to process the stream pdf (IFile contains Name, Stream,Metadata, etc of the PDF)
What can I do to proccess this and whe I click show the stream at a new browser?
I have another probelm, is not working the OnClientClick="aspnetForm.target='_blank';" timbck2 suggested me. I have to open the file (image or pdf ) in a new window, what could I do? Is not working this. Thank!
Timbbck2 my asp code is:
<asp:LinkButton runat="server" ID="LinkButtonFile" OnClick="LinkButtonFile_Click" OnClientClick="aspnetForm.target = '_blank';"></asp:LinkButton>
Thanks!!!
I've done this from a file on disk - sending the data from a memory stream shouldn't be that much different. You need to supply two pieces of metadata to the browser, in addition to the data itself. First you need to supply the MIME type of the data (in your case it would be application/pdf), then the size of the data in bytes (integer) in a Content-Length header, then send the data itself.
So in summary (taking into account the comments and what I think you're asking, your markup would look like this:
<asp:LinkButton ID="whatever" runat="server" OnClick="lnkButton_Click"
OnClientClick="aspnetForm.target='_blank';">your link text</aspnet:LinkButton>
These few lines of C# code in your code behind should do the trick (more or less):
protected void lnkButton_Click(object sender, EventArgs e)
{
Response.ClearContent();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "inline; filename=" + docName);
Response.AddHeader("Content-Length", docSize.ToString());
Response.BinaryWrite((byte[])docStream);
Response.End();
}
To display pdf directly in the browser you should set content type for the response to application/pdf
Your user should also have some kind of pdf reader installed for this to work.
Did you try Response.BinaryWrite?? You probably want to set some headers as well.
The top answer suggests converting (casting) a stream to a byte[] first, that will download the file to the server (from whatever stream it is coming from) and then send it to the client.
If you need to provide a Stream as a parameter for some external service (like me for Azure Storage) you can use the following method.
public ActionResult Coupons(string file)
{
Response.AddHeader("Content-Disposition", "inline; filename=" + file);
Response.ContentType = "application/pdf";
AzureStorage.DownloadFile(file, Response.OutputStream);//second parameters asks for a 'Stream', you can use whatever stream you want here, just put it in the Response.OutputStream
return new EmptyResult();
}
That will Stream the file to the client directly (as far as I know).

Dynamically Created PDF Not Embedding/Rendering Inline in 64-bit IE9 Window

We're using Reporting Services to generate a PDF report and we need it to be rendered out to the browser window AND embedded in the browser. We've been doing this a long time and it has always worked ... until IE9.
In IE6, IE7, and IE8, we generate the byte array from Reporting Services that represents the PDF report and binary write it out to the browser, and everything works great. The PDF displays embedded in the browser.
In IE9, we try the same exact thing and the PDF is NOT embedded in the browser window. The browser window stays open and is blank/empty, and the PDF is opened in Adobe Reader in a separate window.
Here's a snippet of our code:
try
{
// Set all the Reporting Services variables and parameters and render the report
// ...
byte[] result = rs.Render(format, devInfo, out extension, out mimeType, out encoding, out warnings, out streamIDs);
// Force the render out of the report to the browser
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.AppendHeader("content-length", result.Length.ToString());
Response.AppendHeader("Pragma", "cache");
Response.AppendHeader("Expires", "3600");
Response.Buffer = true;
Response.Cache.SetCacheability(HttpCacheability.Private);
Response.CacheControl = "private";
Response.Charset = System.Text.UTF8Encoding.UTF8.WebName;
Response.ContentEncoding = System.Text.UTF8Encoding.UTF8;
switch (outputformat)
{
case "PDF":
Response.AppendHeader("content-disposition", "inline; filename=report.pdf");
Response.ContentType = "application/pdf";
break;
default:
break;
}
Response.BinaryWrite(result);
Response.Flush();
Response.Close();
Response.End();
}
catch (System.Exception ex)
{
// ...
}
What can we do to get the PDF rendered and embedded in the IE9 broswer window?
Thanks!
hope this might help others, I found a hack/work around of sorts using jquery
function MyFunction(someprams) {
if (navigator.userAgent.indexOf("x64") == -1) {
window.open("yourpage2.aspx?PramName=PramVal, 'winowname', 'window opions here')
} else {
$.get("yourpage1.aspx", { PramName1: PramVal1, PramName1: PramVal1 },
function(data) {
$('#divid').html(data);
});
}
}
so then just add a div to the page :
yourpage1 is the page what calls and streams the pdf and set the cache headers
then yourpage2 is a aspx page that has an ifram on it that i set the src dynamicly :
iframeid.Attributes.Add("src", "yourpage1.aspx?"pram1=" & Request.QueryString("PramVal1") )
note the ifram need a run at server tag, aslo ur probally want it to load the ifram height 100% which u can do
css:
html { height: 100%;}
body { height: 100%;}
html:
<iframe id="iframeid" runat="server" scrolling="no" marginwidth="0" marginheight="0"
frameborder="0" vspace="0" hspace="0" style="overflow: visible; width: 100%;
height: 100%;"></iframe>
what this dose is if user is IE 32 bit then there get a new window open showing the pdf in it (actually in an frame but u cant tell) or if they are IE 64 then skip using window at all and load the page that streams the pdf directly into our page. This forces adobe pdf to open not in a browser window but directly as a pdf so all in all this works and looks ok in both 64 & 32 IE
i did find stream readers caused issues but this works nicely ,
Dim oWebClient As System.Net.WebClient = Nothing
Dim data() As Byte
try
oWebClient = New System.Net.WebClient
data = oWebClient.DownloadData(pdfurl)
//add Response.AddHeader stuff here e.g.
Response.AddHeader("Content-Length", data.Length.ToString)
Response.BinaryWrite(data)
Internet Explorer 64bit can run only 64bit plugins. The Adobe PDF plugin is 32bit and it cannot run in 64bit IE.
Take a look at this forum post:
http://forums.adobe.com/message/3331557#3331557#3331557
Also, this whole thread talks about different fixes to make different versions of IE work. There are multiple things that can cause this issue.
http://forums.adobe.com/thread/758489
One reader also noted that it MUST end in PDF, which it looks like you are doing.
Keep in mind, if you were using different versions of acrobat reader, this issue could actually be related to changes in Reader, and not IE.
In your comment, you noted a 64bit issue. Check out this SO answer re IE8/64bit vista:
Can't display PDF from HTTPS in IE 8 (on 64-bit Vista)
It appears that you're already doing everything that he said he needed to do, in his final answer (namely, setting Cache control to private, and not setting Pragma: no-cache.)
It's interesting to note, that the various responses have gone the way of manually adding the header via:
response.setHeader("Cache-Control","private");
Instead of calling
Response.Cache.SetCacheability(HttpCacheability.Private);
Response.CacheControl = "private";
Unsure there's a difference, but it might be worth a shot.
It is important to note that displaying PDF inline can actually be controlled through Acrobat Reader settings.
In the menu Edit > Preferences..., select Internet from the left-hand navigation and ensure that Display PDF in browser is checked.
It appears that there is a bug in 64 bit IE9; if the file extension being used for the outputStream of the PDF file to be displayed is in UPPERCASE e.g. 'myFile.PDF' instead of lowercase like 'myFile.pdf', the mimeType of the outputStream is not being recognized as application/pdf. It defaults to mimeType text instead. This kind of page does not render at all, or renders partially or renders in some unreadable font. Try using the lowercase .pdf file extension in case it is in uppercase.
Good Luck!

ASHX Image Download on Click

I am using ashx to serve images from a database, is there anyway to have a user click on a link that allows them to download the file on the computer. (IE it shows the Save Dialog) Like you download a file. Is this possible to do?
If you want it to prompt to save make sure you add the following line when creating the response:
context.Response.AppendHeader("Content-Disposition",
"attachment;filename=" + filename);
This will make the browser treat it like an attachment and prompt with the save dialog.
EDIT: Based on your comment make sure you are building your response correctly:
// set attachment header like above
// then you need to get your file in byte[] form
byte[] dataYouWantToServeUp = GetData();
// you can set content type as well
yourHttpContext.Response.ContentType = "image/jpg";
// serve up the response
yourHttpContext.Response.BinaryWrite(dataYouWantToServeUp);
I think that if the navigator is able to open type of the file you're trying to get, It will open it without asking. If for instants, the file is in zip format, the navigator is not able to open it and will ask you to download it.

ASP.NET - Sending a PDF to the user

I have a process that creates a PDF. I want these PDF's to be temporary and short lived. I want to be able to perform the following when the user clicks a button:
string CreatePDF()//returns fileName.pdf
PromptUserToDownloadPDF()
DeletePDF(fileName.pdf)
I want to avoid having to create a cleanup procedure and deal with any race conditions that arise from users concurrently creating PDF's while running cleanup.
In winforms, I would synchronously prompt a user to download a file. How can I do a similar task in web?
UPDATE
Please note that I am using a 3rd party app to create the PDF's (Apache FOP). Basically I (will) have a function that invokes the command line:
C:>fop "inputfile" "output.pdf"
So, in memory is not an option...that is unless I could somehow do like....
string CreatePDF()//returns fileName.pdf
string RecreatePDFInMemory()
DeletePDF(fileName.pdf)
PromptUserToDownloadPDF()
Something like this:
byte[] _pdfbytes = CreatePDF();
Response.ContentType = "application/pdf";
Response.AppendHeader("Content-Length", _pdfbytes.Length.ToString());
Response.BinaryWrite(_pdfbytes);
Since this creates the PDF in memory, you don't need to worry about cleanup.
Edit for OP's edit:
From within CreatePDF, You can use Path.GetTempFileName to create a temp file and execute "fop" to write to that file. Delete that file immediately before returning the byte[]. I recommend doing this delete inside of a finally block. However, "Fop" does support having its output piped to stdout. Having the CreatePDF function grab that is probably cleaner.
Look into doing something along these lines.
Similar to what someone referred to in a different answer, you don't need to save the PDF file on your system, you can just send it as a response.
I'm not sure how you're creating your PDF, but try looking into this below and seeing if your process could use something like this.
HttpResponse currentResponse = HttpContext.Current.Response;
currentResponse.Clear();
currentResponse.ClearHeaders();
currentResponse.ContentType = "application/pdf";
currentResponse.AppendHeader("Content-Disposition", "attachment; filename=my.pdf");
//create the "my.pdf" here
currentResponse.Flush();
currentResponse.End();
Not sure of your process but you should be able to write the PDF to a byte[] and skip writing to the disk altogether.
byte[] pdf = GetPDFBytes(filename)
MemoryStream pdfStream = new MemoryStream(pdf);
Then use the pdfStream to send back to a user.
You can stream out a file with an asp.net page.
I tried to find very old article for you which demonstrates this with a GIF (there's not an actual file)
http://www.informit.com/articles/article.aspx?p=25487
It makes a special page which streams out the data (sets content type appropriately in the header).
Similarly, you can make a "page" to stream out the PDF - it might not even need to ever reside on disk, but if it did, you could delete it after streaming it to the browser.

Categories

Resources