1

I need to track when a pdf is opened in my web app. Right now I am writing to a database when a user clicks on the link and then using window.open from the code behind which isn't ideal since Safari blocks popups and other web browsers give a warning when it runs so I was thinking would a Filehandler be what I need to use. I haven't used a Filehandler in the past so is this something that would work? The pdf is not in binary form, it's just a static file sitting in a directory.

MikeSmithDev
  • 15,731
  • 4
  • 58
  • 89
Sam Cromer
  • 687
  • 3
  • 12
  • 31
  • 1
    @patxy An `IHttpHandler` implementation can and has been used for this sort of thing, to be sure. – Grant Thomas Oct 01 '13 at 19:12
  • I'm just looking for a way to write to the db and then open the file in another tab without having to use window.open – Sam Cromer Oct 01 '13 at 19:20
  • @SamCromer if you don't want to make your own HttpHandler, then just change the link to a link button, execute the code you need on postback, and redirect the user to the document. Otherwise, consider [reading this](http://msdn.microsoft.com/en-us/library/ms972953.aspx). – MikeSmithDev Oct 01 '13 at 19:23
  • @MikeSmithDev , that is what I am currently doing but popup blockers are ruining the party, safari doesn't even give a warning it just doesn't work. Here is what I did: protected void AccessFile_Click(object sender, EventArgs e) { in all versions of Safari string newWindowUrl = "pdf/Newsletter01.pdf#zoom=100"; string javaScript = "\n"; this.RegisterStartupScript("", javaScript); } – Sam Cromer Oct 01 '13 at 19:45

2 Answers2

4

Here is an option for a custom HttpHandler that with use a regular anchor tag to a PDF:

Create the ASHX (Right-click your project -> Add New Item -> Generic Handler)

using System.IO;
using System.Web;

namespace YourAppName
{
    public class ServePDF : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            string fileToServe = context.Request.Path;
            //Log the user and the file served to the DB
            FileInfo pdf = new FileInfo(context.Server.MapPath(fileToServe));
            context.Response.ClearContent();
            context.Response.ContentType = "application/pdf";
            context.Response.AddHeader("Content-Disposition", "attachment; filename=" + pdf.Name);
            context.Response.AddHeader("Content-Length", pdf.Length.ToString());
            context.Response.TransmitFile(pdf.FullName);
            context.Response.Flush();
            context.Response.End();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

Edit the web.config to use your Handler for all PDFs:

<httpHandlers>
    <add verb="*" path="*.pdf" type="YourAppName.ServePDF" />
</httpHandlers>

Now regular links to PDFs will use your handler to log activity and serve the file

<a href="/pdf/Newsletter01.pdf">Download This</a>
MikeSmithDev
  • 15,731
  • 4
  • 58
  • 89
  • Yeah I considered doing Response.Redirect but I dont want them to leave the site. – Sam Cromer Oct 01 '13 at 20:06
  • Well if opening a new window isn't an option, and keeping them in the same tab/page isn't an option.... what were you planning on doing? – MikeSmithDev Oct 01 '13 at 20:10
  • i think I mentioned in an earlier post that new tab is what I need Response.Redirect opens in the same tab/window which is not an option. "open the file in another tab without having to use window.open " – Sam Cromer Oct 01 '13 at 20:18
  • @SamCromer That wasn't really clear. I know you already accepted the other answer, but here is an option that I think is a little easier to use since you aren't messing with IDs, etc. Here for the community. – MikeSmithDev Oct 01 '13 at 20:55
  • I added the Generic Handler but it gives 2 errors that I can't find any information on: A namespace cannot directly contain members such as fields or methods and Keyword, identifier, or string expected after verbatim specifier: @ – Sam Cromer Oct 02 '13 at 14:20
  • @SamCromer It's almost the same code as in the answer you marked as accepted. My `ProcessRequest()` is just slightly different and I included the `web.config` entry so you don't have to link directly to the ashx file, and instead just use the regular pdf link. – MikeSmithDev Oct 02 '13 at 14:27
  • No I am getting these compile errors. A namespace cannot directly contain members such as fields or methods and Keyword, identifier, or string expected after verbatim specifier: @ – Sam Cromer Oct 02 '13 at 14:37
  • @MikeSmithDev, nice technique. I like that in all outward aspects it appears to be a direct link to the requested resource. – JMD Feb 06 '15 at 22:08
  • @JMD thanks... I like this method too. Opens up a lot of possibilities for custom file handling. – MikeSmithDev Feb 06 '15 at 23:39
  • 1
    You might consider using context.ApplicationInstance.CompleteRequest instead of context.Request.End. See these links for more info. http://stackoverflow.com/questions/7867238/alternative-to-response-end, Recommendations on ending requests - http://blogs.msdn.com/b/aspnetue/archive/2010/05/25/response-end-response-close-and-how-customer-feedback-helps-us-improve-msdn-documentation.aspx, and How to use CompleteRequest without sending unwanted data - http://stackoverflow.com/questions/1087777/is-response-end-considered-harmful/3917180#3917180 – Bryan Feb 25 '15 at 16:47
4

Create an ASHX (faster than aspx onload event) page, pass a the id of the file as a querystring to track each download

 public class FileDownload : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
        {
            //Track your id
            string id = context.Request.QueryString["id"];
            //save into the database 
            string fileName = "YOUR-FILE.pdf";
            context.Response.Clear();
            context.Response.ContentType = "application/pdf";
            context.Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
            context.Response.TransmitFile(filePath + fileName);
            context.Response.End();
           //download the file
        }

in your html should be something like this

<a href="/GetFile.ashx?id=7" target="_blank">

or

window.location = "GetFile.ashx?id=7";

but I'd prefer to stick to the link solution.

pedrommuller
  • 15,741
  • 10
  • 76
  • 126
  • Thanks this is what I was getting at, I haven't used a FileHandler before and I was wondering if it would work for this issue. Thanks! – Sam Cromer Oct 01 '13 at 20:19
  • 1
    yeah that question have been there for a while and a ashx will solve your situation very easily. – pedrommuller Oct 01 '13 at 20:22