Django下载文件

69

我对使用Django相当陌生,我正在尝试开发一个网站,用户可以上传多个Excel文件,这些文件将被存储在一个媒体文件夹中Webproject/project/media

def upload(request):
    if request.POST:
        form = FileForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return render_to_response('project/upload_successful.html')
    else:
        form = FileForm()
    args = {}
    args.update(csrf(request))
    args['form'] = form

    return render_to_response('project/create.html', args)

然后该文档将与他们上传的任何其他文档一起显示在列表中,您可以单击进入其中一个文档并显示有关它们以及上传的Excel文件名称的基本信息。从这里,我希望能够使用链接再次下载相同的Excel文件:

 <a  href="/project/download"> Download Document </a>

我的网址是

 urlpatterns = [

              url(r'^$', ListView.as_view(queryset=Post.objects.all().order_by("-date")[:25],
                                          template_name="project/project.html")),
              url(r'^(?P<pk>\d+)$', DetailView.as_view(model=Post, template_name="project/post.html")),
              url(r'^upload/$', upload),
              url(r'^download/(?P<path>.*)$', serve, {'document root': settings.MEDIA_ROOT}),

          ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

但是我遇到了错误,serve()收到了一个未预期的关键字参数 'document root'。有人可以解释如何修复这个问题吗?

或者

请说明如何使用上传文件并进行选择和服务。

def download(request):
    file_name = #get the filename of desired excel file
    path_to_file = #get the path of desired excel file
    response = HttpResponse(mimetype='application/force-download')
    response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
    response['X-Sendfile'] = smart_str(path_to_file)
    return response

1
你能把serve视图的代码包含进来吗? - xthestreams
14个回答

138

你在document_root参数中漏掉了下划线。但是在生产环境中使用serve不是一个好主意。相反,使用像这样的东西:

import os
from django.conf import settings
from django.http import HttpResponse, Http404

def download(request, path):
    file_path = os.path.join(settings.MEDIA_ROOT, path)
    if os.path.exists(file_path):
        with open(file_path, 'rb') as fh:
            response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
            response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
            return response
    raise Http404

2
这不会让用户使用类似于“../../some/other/path/secrets.txt”这样的“路径”下载服务器上的任何文件吗? - Don Kirkby
2
能否调整这个方法,使用户可以下载.exe文件? - Keselme
@Keselme 是的。你只需要将 content_type 改为 'application/octet-stream'。 - Sergey Gornostaev
@SergeyGornostaev,谢谢您。如果文件是.apk类型的,我应该使用'application/vnd.android.package-archive'作为内容类型,还是'application/octet-stream'仍然可以? - Keselme
2
我来晚了,但是:path参数是什么?我能用它下载PowerPoint文件吗? - Micromegas
显示剩余7条评论

66

3
这么容易实现,为什么点赞这么少呢! - thatman303
8
因为它与Django不兼容。 Django将“/project/download”视为URL,因此如果“/project/myfile.pdf”不能解析为URL,则GET请求将在Django中返回404错误,即使HTML标记为下载也是如此。 - Hanny
3
它在我的 Django 中运行。 <a href="{{ obj.file_field_name.url }}" download>下载</a> - Harun-Ur-Rashid
@Hanny,你确定下载标签没有解决你的问题吗?也许你可以检查一下你的MEDIA_ROOT文件夹设置。 - Hasan Basri
对我也管用,我只需要在models.py中的FileFieldupload_to属性中更改路径即可。我的模板看起来像这样:<a href="{{obj_name.field_name.url}}" download> - Danny
显示剩余3条评论

42

参考文献:

在view.py中实现如下函数:

def download(request, id):
    obj = your_model_name.objects.get(id=id)
    filename = obj.model_attribute_name.path
    response = FileResponse(open(filename, 'rb'))
    return response

2
这个看起来比被接受的答案更好,谢谢!这个在某种程度上也适用于nginx吗? - Johannes Pertl
在引用内部,但值得一提的是,不需要上下文管理器:“文件将自动关闭,因此不要使用上下文管理器打开它。” - Djones4822

8
当使用 `FileField` 上传文件时,该文件将具有 URL,您可以使用该 URL 指向文件并使用 HTML 的 `download` 属性下载该文件,您只需这样做即可。
`models.py`
`models.py` 如下所示。
class CsvFile(models.Model):
    csv_file = models.FileField(upload_to='documents')

views.py

#csv文件上传

class CsvUploadView(generic.CreateView):

   model = CsvFile
   fields = ['csv_file']
   template_name = 'upload.html'

#CSV 下载

class CsvDownloadView(generic.ListView):

    model = CsvFile
    fields = ['csv_file']
    template_name = 'download.html'

然后在您的模板中。
#上传模板 upload.html
<div class="container">
<form action="#" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.media }}
    {{ form.as_p }}
    <button class="btn btn-primary btn-sm" type="submit">Upload</button>
</form>

#下载模板

download.html

  {% for document in object_list %}

     <a href="{{ document.csv_file.url }}" download  class="btn btn-dark float-right">Download</a>

  {% endfor %}

我没有使用表单,只是渲染了模型,但无论哪种方法,FileField 都在那里,它会工作得一样。


经过三年多的时间,对我来说这仍然是一件简单明了的事情。谢谢。 - undefined

4
我发现Django的FileField在允许用户上传和下载文件方面非常有用。Django文档中有一个关于管理文件的章节。您可以将一些关于文件的信息存储在表中,并附有指向文件本身的FileField。然后,您可以通过搜索该表来列出可用的文件。

3
  1. <a href='/your-download-view/' download>下载</a>

  2. 在您的视图中:

from django.http import FileResponse

def download(request):
    # pre-processing, authorizations, etc.
    # ...
    return FileResponse(open(path_to_file, 'rb'), as_attachment=True)

如果返回的文件扩展名不同怎么办?例如,我有一个PDF文件,然后将其转换为JSON格式进行处理,最后下载结果并保存到计算机上。 - perymerdeka

2
import mimetypes
from django.http import HttpResponse, Http404

mime_type, _ = mimetypes.guess_type(json_file_path)
    
if os.path.exists(json_file_path):
    with open(json_file_path, 'r') as fh:
        response = HttpResponse(fh, content_type=mime_type)
        response['Content-Disposition'] = "attachment; filename=%s" % 'config.json'
        return response
raise Http404

2

@Biswadp的解决方案对我非常有帮助

在您的静态文件夹中,确保拥有用户需要下载的所需文件

在您的HTML模板中,您的代码应该如下所示:

<a href="{% static 'Highlight.docx' %}"> Download </a>

确保在页面顶部加载静态文件 {% load static %},紧接着{% block content %}。这是最简单的方法,对我来说效果非常好且速度很快。 - Rene Chan

2
使用下面的方法会使安全性降低,因为任何用户都可以访问到其他用户的文件。
<a  href="/project/download" download> Download Document </a>

使用以下方法是没有意义的,因为Django一次只处理一个请求(除非您使用gunicorn或其他工具),相信我,以下方法需要很长时间才能完成。
def download(request, path):
    file_path = os.path.join(settings.MEDIA_ROOT, path)
    if os.path.exists(file_path):
        with open(file_path, 'rb') as fh:
            response = HttpResponse(fh.read(), content_type="application/vnd.ms-excel")
            response['Content-Disposition'] = 'inline; filename=' + os.path.basename(file_path)
            return response
    raise Http404

那么最佳解决方案是什么呢? 使用Nginx认证路由。当从Nginx请求文件时,您可以向路由发出请求,并根据HTTP响应,Nginx允许或拒绝该请求。这使其非常安全,同时也具有可扩展性和高性能。 您可以在此处了解更多信息。

1
如果文件是模型中的FileField,我会按照以下方式处理:
    try:
        download_file = PrintingFile.objects.get(pk=kwargs.get('pk_file', 0))
        return FileResponse(download_file.file.open(), as_attachment=True)
    except PrintingFile.DoesNotExist:
        raise Http404

这里有更多相关的IT技术内容。


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