使用XMLHttpRequest在Django中通过按钮下载文件

3
我是一名能够翻译文本的助手。
我看了许多类似的问题,但没有一个能解决我的问题。我正在尝试使用按钮下载在我的视图中生成的文件。
问题:点击下载按钮时,我得到一个空文件。
我的视图:
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')

我的网址:

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

我的模版:

 {% 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 %}

我的Django视图正在渲染响应。我在UI中的class为“dump”的按钮上添加了一个点击处理程序,以便通过XHR请求访问“/dump” URL,并作为blob接收响应,使用自定义名称和日期下载文件。
我的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();
});

你在视图中如何使用 'response' 变量? - Neeraj Kumar
我不返回响应,这样我就可以使用我的模板。如果我只是返回响应,那么我可以直接下载文件,而没有我的模板和按钮。 - c.ceti
2个回答

6
在您看来,我建议在响应中添加一个大小。
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'

正如 @bob-vork 刚才所说,从 Content-Disposition 中获取的文件名不应该是 Python 的 file 对象,而是你要下载的文件名。

在 urls.py 中注册视图,然后将 JavaScript 替换为:

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

编辑:

既然您要使用建议的HTML,那么我将对我的解决方案进行更多详细说明。

首先,您可以放弃 home.js。正如您所理解的那样。

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

变得

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

你的views.py应该直接返回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

请注意正确设置 content_type。请注意,我尚未测试视图(特别是 FileWrapper),我改编了一些用于纯文本的代码。这也是为什么如果您将pdf视为文本文件,则会出现TypeError。

在 urls.py 文件中:

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

谢谢您的回答,我已经按照您的建议替换了JavaScript和URL为:"url(r'^dump/', dumpView.as_view(), name='download_my_pdf')",但是当我按下按钮时无法下载文件。 - c.ceti
当我像你所说的那样使用content和getsize()时,我得到了一个TypeError“ coercing to Unicode: need string or buffer, file found”。 - c.ceti

0
在你的代码中,你将Content-Disposition设置为attachment; filename=...,但是你附加了文件对象。我猜测浏览器可能需要一个带有正确扩展名的文件名。

我在Django项目中也持有类似的看法,并且我也引用文件名,但我不确定是否必要。

另外,在单词 "attachment" 中存在一个错别字,请删除多余的 "e" 并检查是否有所帮助。


谢谢您的回答,但这并没有解决问题。我尝试了以下两种方法:"response['Content-Disposition'] = 'attachement; filename=%s' % test.pdf" 和 "response['Content-Disposition'] = 'attachement; filename=%s' % /home/test.pdf",但是我得到的是一个空文件。 - c.ceti
我认为我的JS没有从视图接收到响应,因为当我尝试返回响应时,我可以成功下载文件,而不需要模板或按钮。 - c.ceti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接