47

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
0

13 Answers 13

55

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.

Sign up to request clarification or add additional context in comments.

Comments

39

Simplistically, if you have a PDF file and want to output it through a Django view, all you need to do is to 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

You can probably just return the response directly without specifying Content-Disposition, but that better indicates your intention and also allows you to 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.

4 Comments

It doesn't work for me until i've changed mimetype='application/pdf to contenttype='application/pdf'
content_type, also pdf.closed has no effect there
@azuax It's in a context processor, doesn't a return have the __exit__ call?
It would be better to use rb instead of r, as PDF files are not text files.
16

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

2 Comments

Opening and showing the pdf worked for me using 'r'.
Even on non-Windows machines, it is best to use rb not r, as PDF files are not text files.
8

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

Comments

8

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

Comments

4

Here is a typical use-case for displaying a PDF using class-based views:

from django.contrib.auth.decorators import login_required
from django.http import HttpResponse

class DisplayPDFView(View):

    def get_context_data(self, **kwargs):  # Exec 1st
        context = {}
        # context logic here
        return context

    def get(self, request, *args, **kwargs):
        context = self.get_context_data()
        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = 'inline; filename="worksheet_pdf.pdf"'  # Can use attachment or inline

        # pdf generation logic here
        # open an existing pdf or generate one using i.e. reportlab

        return response

# Remove login_required if view open to public
display_pdf_view = login_required(DisplayPDFView.as_view())

For generating your own pdf with reportlab see the Django project Docs on PDF Generation.

Chris Pratt's response shows a good example of opening existing PDFs.

Comments

0

Browsers aren't PDF readers (unless they have the proper plugin/addon).

You may want to render the PDF as HTML instead, which can be done from the backend or the frontend.

1 Comment

All the major browsers as of 2019 have native PDF support, apart from IE.
0

it worked for me

import re, os
import os
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def export_auto_doc(request):
    name = request.GET.get('name', "")
    filename = "path/to/file"+name+".pdf"
    try:
        if not re.search("^[a-zA-Z0-9]+$",name):
            raise ValueError("Filename wrong format")
        elif not os.path.isfile(filename):
            raise ValueError("Filename doesn't exist")
        else:
            with open(filename, 'r') as pdf:
                response = HttpResponse(pdf.read(), content_type='application/pdf')
                response['Content-Disposition'] = 'inline;filename='+name+'.pdf'
                return response
            pdf.closed
    except ValueError as e:
        HttpResponse(e.message)

Comments

0

The easiest way to do this is probably with an anchor in a template. For example, if you are using Django's templating engine (as most people who search for this probably are), simply serve it as a static file through an anchor.

In your template that will contain a link to the file, add at the very top

{% load static %}

Then, wherever you want to link to your pdf, put

<a href="{% static 'relative/path/file.pdf' %}">Click me</a>

The first line tells Django to look in the directories configured for static files in settings.py. The path that you use in the anchor tag is relative to any of the directories that you configured as static directories in settings.py. When you click the rendered link, it should display the PDF in your browser, provided you have your static files pathed correctly.

1 Comment

Oh, my bad. This doesn't exactly answer the question. This doesn't display the PDF in a view...
0

I am just throwing this out there.

You can simply add your PDF resume to your static files.

If you are using White Noise to serve your static files, then you don't even need to make the view. Just then access your resume at the static location.

I added mine, here it is: TIm-D_Nice.pdf

Warning: This doesn't solve the login_required requirement in the question

1 Comment

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' %}"
0

Use iframe url=url of pdf tag and give url of that pdf and make sure that your user will have full control of the project then pdf will be displayed on web screen

Comments

0

In case you want to serve file on both local and online server you encounter issue file not found due to different path you can try this method

from django.core.files.storage import FileSystemStorage
from django.http import HttpResponse
import os

     def view(request, filename):
        BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        fs = FileSystemStorage(BASE_DIR+'/media')
        if fs.exists(filename):
            with fs.open(filename) as pdf:
                response = HttpResponse(pdf, content_type='application/pdf')
                response['Content-Disposition'] = 'inline; filename="' + filename + '"'
                return response
        else: 
            return HttpResponse("Invalid file")

Comments

0
def pdf_view(request,pdfSlug):
     a = Pdf.objects.get(pdf_slug=pdfSlug)
     with open(str(a.pdf_file.path), 'rb') as pdf:
         response = FileResponse(pdf.read(), content_type='application/pdf')
         response['Content-Disposition'] = 'filename=a.pdf'
         return response

This worked for me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.