c.ceti
c.ceti

Reputation: 203

Download a file in django with a button using xmlhttprequest

I looked at many same questions but none resolved my problem, I'm trying to download a file generated in my view using a button. Problem : I get an empty file while clicking on download button

My view:

class dumpView(View):

template_name = 'download.html'
def get(self, request):
    file = open("/home/test.pdf", "rwbt")
    response = HttpResponse(file.read(), content_type="application/pdf")
    response['Content-Disposition'] = 'attachement; filename=%s' % file
    return render(request, 'download.html')

My url:

url(r'^dump/', dumpView.as_view()),

My template:

 {% extends 'base.html' %} 
 {% load staticfiles %} 
 {% block content %}
 <div class="container">
 <div class="row">
    <div class="jumbotron">
        <div class="row">
            <center>
                <a href="javascript:void(0)" class="btn btn-primary btn-lg dump">Download</a>
            </center>
        </div>
    </div>
</div>
</div>
{% endblock %} 
{% block javascript %}
<script src="{% static 'js/home.js' %}"></script>
{% endblock %}

My Django view is rendering the response. I did add an on click handler to the button with class dump in the UI to make a XHR request to the “/dump” URL and recieve response as a blob and download the file with custom name and date.

My js :

$(".dump").on("click", function() {
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a, today;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        today = new Date();
        a.download = "file_" + today.toDateString().split(" ").join("_") + ".pdf";
        a.style.display = 'none';
        document.body.appendChild(a);
        return a.click();
    }
};
xhttp.open("GET", "/dump", true);
xhttp.setRequestHeader("Content-Type", "application/json");
xhttp.responseType = 'blob';
xhttp.send();
});

Upvotes: 4

Views: 21303

Answers (2)

Victor T
Victor T

Reputation: 408

In your view, I suggest adding a size to your response

content = file(filename).read()
response = HttpResponse(content, content_type='text/plain')
response['Content-Length'] = os.path.getsize(filename)
response['Content-Disposition'] = 'attachment; filename=%s' % 'my_pdf.pdf'

As @bob-vork just said, the filename from Content-Disposition should not be the python file object, but the name your download will have.

How about registering the view in urls.py then replace the javascript by:

<input type="button" value="Download" onclick="window.open('download_my_pdf')">

EDIT :

Since you're going with the suggested HTML, then I'll put more details to my solution.

First, you can drop the home.js. As you understood

<a href="javascript:void(0)" class="btn btn-primary btn-lg dump">Download</a>

becomes

<input type="button" value="Download" onclick="window.open('download_my_pdf')">

your views.py should return directly the response

from django.http import HttpResponse
from wsgiref.util import FileWrapper

@login_required
def download_pdf(request):
    filename = 'whatever_in_absolute_path__or_not.pdf'
    content = FileWrapper(filename)
    response = HttpResponse(content, content_type='application/pdf')
    response['Content-Length'] = os.path.getsize(filename)
    response['Content-Disposition'] = 'attachment; filename=%s' % 'whatever_name_will_appear_in_download.pdf'
    return response

Pay attention to put the right content_type as well. Note that I haven't tested the view (especially the FileWrapper), I adapted some code I had for plain text. This is also why you have a TypeError if you treat the pdf as a text file.

In urls.py

urlpatterns = (
    [...]
    url('download_my_pdf', download_pdf),
)

Upvotes: 6

Bob Vork
Bob Vork

Reputation: 2947

In your view, you set Content-Disposition as attachment; filename=... but you append the file object. My guess is the browser might want a filename, preferably with the right extension.

I have a similar view in a Django project, and I'm quoting the filename as well, but I'm not sure if that is necessary

EDIT: also, there is a typo in "attachment", remove the extra "e" and see if that helps

Upvotes: 0

Related Questions