5

I'm currently using an html embed tag to display a pdf file that is saved on the local server. Is there a wayo to display a pdf file on my page without having to save it to the local file system of the server? I just wand to pass it to the view from the controller in such a way that it can be displayed as a pdf in the page without having it stored on the file system directly.

Alternatively, is there a way to call a method to delete the pdf file from the server once the user has navigated away from the page they are viewing? How do I tell if th euser has navicated away from the page and how do i cause that to trigger a method that will delete the file?

Aaron Patten
  • 115
  • 1
  • 2
  • 10
  • 1
    you can just stream it from memory... although that wouldn't scale very well... – Yahia Oct 12 '11 at 23:35
  • Are you open to third party tools? – Steve Danner Oct 12 '11 at 23:45
  • You are generating this PDF file I assume? If so, you should be able to stream it directly into the response from whatever format you are processing it with (e.g. iTextSharp). – Reddog Oct 12 '11 at 23:47
  • 1
    Im uploading and storing pdf files in a database and want to know how to properly store and retrieve them for display in my view without having to save them to the file system. In my oppinoin having to temporarily save them to the file system defeats the purpose of having the database to begin with. – Aaron Patten Oct 14 '11 at 00:01

5 Answers5

4

I created a MVC class called PdfResult that returns a byte array as a PDF file.

The purpose is as follows (can't upload the source code, sorry):

  1. PdfResult inherits from FileStreamResult
  2. Set the Content-Type header to application/pdf
  3. Set the Content-Disposition to either attachment or inline, and set an appropriate file name
  4. Convert your data to a Stream -- if your data is a byte array, then write it to a MemoryStream.

See https://stackoverflow.com/a/16673120/272072 for a good example of how to do this.

Then, your embed code just needs to point to the action method, as if it was a PDF file.
Here's an example:

public ActionResult ShowPdf() {
    // Note: the view should contain a tag like <embed src='MyController/GetPdf'>
    return View(); 
}

public ActionResult GetPdf() {
    byte[] pdfBytes = dataRepo.GetPdf(...);
    return new PdfResult(pdfBytes, "Filename.pdf", false) ;
} 
Community
  • 1
  • 1
Scott Rippey
  • 15,614
  • 5
  • 70
  • 85
  • I think this is what I'm looking for but how do I pass this pdf to the embed code? To I just type the path and name of the method as "/controller/method" and put that inside the src"" tag of the embed code? – Aaron Patten Oct 26 '11 at 19:30
  • That's correct. To embed the PDF in a page, you'll need 2 `Action` methods. The first one will return a normal `View` that will have the `` code in it, and the `src` tag will point to the second `GetPdf` action as if it was an actual PDF file. – Scott Rippey Oct 26 '11 at 20:29
  • I don't have any of the dependancies of that method and my compiler doesn't even sem to understand the syntax. I'm using asp.net mvc3, could this be an issue? How should I construct my own getPDF method; what is required? does it need to return a object of type HttpPostedFileBase? – Aaron Patten Oct 26 '11 at 22:11
  • Error 3 The type or namespace name 'ContentDisposition' could not be found (are you missing a using directive or an assembly reference?) C:\Users\Aaron Patten\Documents\Visual Studio 2010\Projects\MyApp\MyApp\Controllers\MyController.cs 17 16 MyApp – Aaron Patten Oct 26 '11 at 22:42
  • 1
    The `PdfResult` class uses the `ContentDisposition` class, which requires a reference to `System.Net.Mime`. Sorry, that part was missing from the code for brevity. I put the whole [PdfResult.cs file here](https://gist.github.com/1318438), so take a look. – Scott Rippey Oct 27 '11 at 00:24
  • @DFTR Sorry, that Gist was removed a while ago, because my work didn't want me sharing that code. There's not a lot I can do. It wasn't a lot of code, though, the only thing it really did was set the `Content-Type` header to `application/pdf`, and set the `Content-Disposition` with the filename. I've updated my answer. – Scott Rippey Oct 29 '13 at 22:27
2

Here is a link to a CodeProject article and code sample titled Download and Upload Images from SQL Server via ASP.NET MVC. This gives an example of an efficient method to stream content to and from SQL Server via MVC.

You can easily adapt the code to stream your PDF file downloads.

UPDATE

The article uses a DataReader, but it can easily be adapted to Linq2Sql or EF. As an example, here is the Read method where I am reading from the database and copying to the stream:

public override int Read(byte[] buffer, int offset, int count)
{
    result = _attachments.ExecuteStoreQuery<byte[]>(
             "SELECT SUBSTRING(AttachmentBytes, " + position.ToString() +
             ", " + count.ToString() + ") FROM Attachments WHERE Id = {0}",
             id).First();

    var bytesRead = result.Length;
    Buffer.BlockCopy(result, 0, buffer, 0, bytesRead);

    position += bytesRead;
    return (int)bytesRead;
}
counsellorben
  • 10,924
  • 3
  • 40
  • 38
  • Im using linq to sql and the explenation doesn't really make sense to me. I also am unable to get the example project to run. Any suggestions? – Aaron Patten Oct 14 '11 at 00:02
  • I updated my answer to show how I adapted this to use EF. You can do something similar using Linq2Sql in order to read a block from the database into a stream. – counsellorben Oct 14 '11 at 03:15
  • The real crux of the problem was getting it to the view page without putting it anywhere on the server. I'll have a look at how this project does that as well but for now Scott's solution is relatively simple and works. – Aaron Patten Oct 28 '11 at 16:59
1

You can read the PDF as a bytestream from the database and save it to the http response stream. If you have set the content type correctly to application/pdf, then the browser will load the document in the PDF plugin.

Update (14/Oct/2011): You need to write the bytestream to the Response.OutputStream object. How you create and write the byte stream is dependent on how you have stored in the database and how you are retrieving it. The following code snippet is from an article we have on our website - Generate PDF Forms In ASP.NET Using PDFOne .NET v3.

  // Get the page's output stream ready
  Response.Clear();
  Response.BufferOutput = true;
  // Make the browser display the forms document
  // using a PDF plug-in. (If no plug in is available, 
  // the browser will show the File -> Save As dialog box.
  Response.ContentType = "application/pdf"; 

  // Write the forms document to the browser
  doc.Save(Response.OutputStream);

  doc.Close();
  doc.Dispose();

The doc object is from our component. You need not use that. This code snippet is only for your understanding. For your requirement, you may have to something like bytestream.save(Response.OutputStream) I guess. BTW, this code is for ordinary ASP.NET, not MVC.

DISCLAIMER: I work for Gnostice.

BZ1
  • 1,306
  • 2
  • 8
  • 10
  • Coul you direct me to a resource that woudl explain how to do al three of these steps? – Aaron Patten Oct 13 '11 at 18:37
  • how do i save it to the http response? How do I display it in the view once it is in the http response? How do I get it from the database in byte stream format? – Aaron Patten Oct 14 '11 at 00:03
  • In ordinary ASP.NET, you write the bytestream to the Response.OutputStream after setting the content type to application/pdf. The response code can work in a separate page or an IFRAME. The PDF is usually stored as a blob object in a database. You need to retrieve that blob first and create a bytestream out of that. It is also dependent on how you have stored the PDF in the database. If have written the code store it, then you will easily know how to extract it. – BZ1 Oct 14 '11 at 04:45
0

If you want to create the PDF 100% dynamically, you would generate it completely in memory then stream it out directly to the requesting web browser without saving it as a file. This is very easy to do with the right tools. I would recommend AspPDF from Persits.com as a way to do this very easily. Take a look at their online documentation to see how simple this is to do without creating a bunch of rendered PDF files all over your server.

If you cannot do something like that, then simply incorporate a process to cleanup your "expired" PDF files from your server's filesystem based on their age. For example, after you have created your local PDF file, you just look through the folder containing your temporary PDF's and delete any you find over a certain age. You cannot reliably tell if or when a user has navigated away from your page or site.

dmarietta
  • 1,940
  • 3
  • 25
  • 32
  • I am no generating the pdf. I am uploading it from a user. It get's uploaded as a file and I would lik eto know how to display it in the view without having to save it to the file system so that I could presumeably save it in a database and retrieve it for display later on without needing to take up server space or worry about having to clean up old files. – Aaron Patten Oct 13 '11 at 00:50
  • @AaronPatten, if it got uploaded as a file in the first place, why don't you just store the file to some location accessible via a URL, and just store the filename in the database. That is a whole lot more scalable, plus usually web server space is a lot more cheaper compared to database space. – Amry Oct 13 '11 at 03:52
  • 1
    The PDF's are to remain confidential and inaccesible to those who aren't registered to have access to them. – Aaron Patten Oct 14 '11 at 00:04
0

For the first part of your question, like mentioned in the comments, use some type of stream object to pass the PDF data around. Right now, you are streaming the file to the local file system, then streaming it once again to the embedded tag for display. Just do away with the intermediate step of saving to the file system, and do the whole thing in memory (although, that's not really a model of efficiency, and might not scale well).

Regarding the second part of your question, that's not as straightforward. MVC really has no concept of state (viewstate, etc.), so it doesn't have events that can be fired from a state change (say, navigating away from a page).

You could use Javascript to detect a user navigating away from your page (windows.onunload), that calls a (C#/VB) method to remove the file from the file system. You would probably have to use AJAX to communicate back to the server, using an HTTP POST method, and have something listening at that URL endpoint to fire your method that removes the file.

Patrick Pitre
  • 843
  • 4
  • 6
  • For the first part of your response: This is exactly what I would like to know how to do. Do you know any good resources I could read that would help me learn how to do this? – Aaron Patten Oct 13 '11 at 00:49
  • Instead of waiting for a Javascript call to signal for a file deletion, which is really not reliable by the way, it's probably better to just delete the files based on some timestamp, say after one day old, or maybe 2 hours old? Of course, this option will require some scheduled task to check the files. – Amry Oct 13 '11 at 03:55
  • @Amry Agreed that a Javascript solution has the potential to be unreliable, although it does meet his requirements, as stated. A scheduled task would be more reliable, but if you're going that route, you might as well use PowerShell or a DOS/VB/CMD-type script, and schedule it to run using Task Scheduler (which, I've found, also isn't all that reliable sometimes). – Patrick Pitre Oct 13 '11 at 16:54