How to show a PDF file in a Django view?

63,861

Solution 1

Django has a class specifically for returning files, FileResponse. It streams files, so that you don't have to read the entire file into memory before returning it. Here you go:

from django.http import FileResponse, Http404

def pdf_view(request):
    try:
        return FileResponse(open('foobar.pdf', 'rb'), content_type='application/pdf')
    except FileNotFoundError:
        raise Http404()

If you have really large files or if you're doing this a lot, a better option would probably be to serve these files outside of Django using normal server configuration.

Solution 2

Simplistically, if you have a PDF file and you want to output it through a Django view, all you need to do is dump the file contents into the response and send it with the appropriate mimetype.

def pdf_view(request):
    with open('/path/to/my/file.pdf', 'r') as pdf:
        response = HttpResponse(pdf.read(), mimetype='application/pdf')
        response['Content-Disposition'] = 'inline;filename=some_file.pdf'
        return response
    pdf.closed

You can probably just return the response directly without specifying Content-Disposition, but that better indicates your intention and also allows you specify the filename just in case the user decides to save it.

Also, note that the view above doesn't handle the scenario where the file cannot be opened or read for whatever reason. Since it's done with with, it won't raise any exceptions, but you still must return some sort of response. You could simply raise an Http404 or something, though.

Solution 3

PDF files must be opened as rb not r.

def pdf_view(request):
    with open('/path / to /name.pdf', 'rb') as pdf:
        response = HttpResponse(pdf.read(),content_type='application/pdf')
        response['Content-Disposition'] = 'filename=some_file.pdf'
        return response

Solution 4

Following @radtek's answer above I decided to investigate a class-based view display. I tried to use View but it didn't have get_context_data() method.

I looked here for some guidance. I settled for BaseDetailView since I wanted to display just one object.

from django.http import FileResponse
from django.shortcuts import get_object_or_404
from django.views.generic.detail import BaseDetailView

class DisplayPdfView(BaseDetailView):
    def get(self, request, *args, **kwargs):
        objkey = self.kwargs.get('pk', None) #1
        pdf = get_object_or_404(Pdf, pk=objkey) #2
        fname = pdf.filename() #3
        path = os.path.join(settings.MEDIA_ROOT, 'docs\\' + fname)#4
        response = FileResponse(open(path, 'rb'), content_type="application/pdf")
        response["Content-Disposition"] = "filename={}".format(fname)
        return response

Commentary

1 This line accesses a named argument pk passed by the url calling the view.

2 This line gets the actual pdf model object.

3 I defined a method filename(self): return os.path.basename(self.file.name) in my model to help me get just the filename plus extension.

4 This line gets the complete filepath.

Then use file response as explained in the answers above. Also remember to use rb to read the pdf file

Solution 5

Take out inline; if you want your file to be read from server. And also, the HttpResponse kwarg mimetype has been replaced by content_type:

(response['Content-Disposition'] = 'inline;filename=some_file.pdf')

def pdf_view(request):
    with open('/app/../Test.pdf', 'r') as pdf:
        response = HttpResponse(pdf.read(),content_type='application/pdf')
        response['Content-Disposition'] = 'filename=some_file.pdf'
        return response
    pdf.closed
Share:
63,861
SImon
Author by

SImon

Updated on July 09, 2022

Comments

  • SImon
    SImon almost 2 years

    Is it possible to show a PDF file in the Django view, rather than making the user have to download it to see it?

    And if it is possible, how would it be done?

    This is what I have so far -

    @login_required
    def resume(request, applicant_id):
    
        #Get the applicant's resume
        resume = File.objects.get(applicant=applicant_id)
        fsock = open(resume.location, 'r')
        response = HttpResponse(fsock, mimetype='application/pdf')
    
        return response
    
  • yndolok
    yndolok over 8 years
    Opening and showing the pdf worked for me using 'r'.
  • Andriy Ivaneyko
    Andriy Ivaneyko over 8 years
    It doesn't work for me until i've changed mimetype='application/pdf to contenttype='application/pdf'
  • amchugh89
    amchugh89 about 8 years
    content_type, also pdf.closed has no effect there
  • TankorSmash
    TankorSmash over 7 years
    @azuax It's in a context processor, doesn't a return have the __exit__ call?
  • medley56
    medley56 about 6 years
    Not sure why this is downvoted. This is what I ended up doing, using {% load static %} and a simple anchor with href="{% url 'file.pdf' %}"
  • medley56
    medley56 about 6 years
    Oh, my bad. This doesn't exactly answer the question. This doesn't display the PDF in a view...
  • Alan B
    Alan B almost 5 years
    All the major browsers as of 2019 have native PDF support, apart from IE.
  • Flimm
    Flimm over 3 years
    Even on non-Windows machines, it is best to use rb not r, as PDF files are not text files.
  • Flimm
    Flimm over 3 years
    It would be better to use rb instead of r, as PDF files are not text files.