让 Django 提供可下载文件

发布于 2024-07-27 23:25:56 字数 250 浏览 6 评论 0原文

我希望网站上的用户能够下载路径被遮挡的文件,因此无法直接下载它们。

例如,我希望 URL 是这样的:http://example.com/download/?f=somefile.txt

在服务器上,我知道所有可下载的文件都驻留在服务器上在文件夹 /home/user/files/ 中。

有没有办法让 Django 提供该文件以供下载,而不是尝试查找 URL 和视图来显示它?

I want users on the site to be able to download files whose paths are obscured so they cannot be directly downloaded.

For instance, I'd like the URL to be something like this: http://example.com/download/?f=somefile.txt

And on the server, I know that all downloadable files reside in the folder /home/user/files/.

Is there a way to make Django serve that file for download as opposed to trying to find a URL and View to display it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

浅唱々樱花落 2024-08-03 23:25:58

我做了一个关于这个的项目。 你可以查看我的 github 仓库:

https://github.com/ nishant-boro/django-rest-framework-download-expert

该模块提供了一种使用 Apache 模块 Xsendfile 在 django Rest 框架中提供文件下载服务的简单方法。 它还具有仅向属于特定组的用户提供下载服务的附加功能

I did a project on this. You can look at my github repo:

https://github.com/nishant-boro/django-rest-framework-download-expert

This module provides a simple way to serve files for download in django rest framework using Apache module Xsendfile. It also has an additional feature of serving downloads only to users belonging to a particular group

十雾 2024-08-03 23:25:57
def qrcodesave(request): 
    import urllib2;   
    url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0"; 
    opener = urllib2.urlopen(url);  
    content_type = "application/octet-stream"
    response = HttpResponse(opener.read(), content_type=content_type)
    response["Content-Disposition"]= "attachment; filename=aktel.png"
    return response 
def qrcodesave(request): 
    import urllib2;   
    url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0"; 
    opener = urllib2.urlopen(url);  
    content_type = "application/octet-stream"
    response = HttpResponse(opener.read(), content_type=content_type)
    response["Content-Disposition"]= "attachment; filename=aktel.png"
    return response 
殤城〤 2024-08-03 23:25:57

上面提到 mod_xsendfile 方法不允许文件名中包含非 ASCII 字符。

出于这个原因,我有一个适用于 mod_xsendfile 的补丁,只要名称经过 url 编码,并且附加标头:

X-SendFile-Encoding: url

也可以发送

,该补丁将允许发送任何文件。 http://ben.timby.com/?p=149

It was mentioned above that the mod_xsendfile method does not allow for non-ASCII characters in filenames.

For this reason, I have a patch available for mod_xsendfile that will allow any file to be sent, as long as the name is url encoded, and the additional header:

X-SendFile-Encoding: url

Is sent as well.

http://ben.timby.com/?p=149

煮酒 2024-08-03 23:25:57

尝试: https://pypi.python.org/pypi/django-sendfile/

“一旦 Django 检查了权限等,就将文件上传抽象到网络服务器(例如 Apache 和 mod_xsendfile)。”

Try: https://pypi.python.org/pypi/django-sendfile/

"Abstraction to offload file uploads to web-server (e.g. Apache with mod_xsendfile) once Django has checked permissions etc."

热风软妹 2024-08-03 23:25:57

您应该在生产中使用 apache 或 nginx 等流行服务器提供的 sendfile api。 多年来我一直使用这些服务器的 sendfile api 来保护文件。 然后为此目的创建了一个简单的基于中间件的 django 应用程序,适合开发和使用。 生产目的。 您可以在此处访问源代码。

更新:在新版本中,python 提供程序使用 django FileResponse(如果可用),并且还添加了对从 lighthttp、caddy 到 hiawatha 的许多服务器实现的支持

用法

pip install django-fileprovider
  • 添加 <将 fileprovider 应用添加到 INSTALLED_APPS 设置,
  • fileprovider.middleware.FileProviderMiddleware 添加到 MIDDLEWARE_CLASSES 设置
  • FILEPROVIDER_NAME 在生产环境中设置为 nginxapache,默认为 python 用于开发目的。

在基于类的视图或函数视图中,将响应标头 X-File 值设置为文件的绝对路径。 例如:

def hello(request):
   # code to check or protect the file from unauthorized access
   response = HttpResponse()  
   response['X-File'] = '/absolute/path/to/file'  
   return response

django-fileprovider 的实现方式使您的代码只需要最少的修改。

Nginx 配置

要保护文件不被直接访问,您可以将配置设置为

location /files/ {
  internal;
  root   /home/sideffect0/secret_files/;
}

此处 nginx 设置位置 url /files/ 仅在内部访问,如果您使用上述配置,您可以将 X-File 设置为:

response['X-File'] = '/files/filename.extension'

通过使用 nginx 配置执行此操作,该文件将受到保护并保存在 X-File 中。 您还可以从 django views 控制文件

You should use sendfile apis given by popular servers like apache or nginx in production. For many years I was using the sendfile api of these servers for protecting files. Then created a simple middleware based django app for this purpose suitable for both development & production purposes. You can access the source code here.

UPDATE: in new version python provider uses django FileResponse if available and also adds support for many server implementations from lighthttp, caddy to hiawatha

Usage

pip install django-fileprovider
  • add fileprovider app to INSTALLED_APPS settings,
  • add fileprovider.middleware.FileProviderMiddleware to MIDDLEWARE_CLASSES settings
  • set FILEPROVIDER_NAME settings to nginx or apache in production, by default it is python for development purpose.

in your class-based or function views, set the response header X-File value to the absolute path of the file. For example:

def hello(request):
   # code to check or protect the file from unauthorized access
   response = HttpResponse()  
   response['X-File'] = '/absolute/path/to/file'  
   return response

django-fileprovider implemented in a way that your code will need only minimum modification.

Nginx configuration

To protect file from direct access you can set the configuration as

location /files/ {
  internal;
  root   /home/sideffect0/secret_files/;
}

Here nginx sets a location url /files/ only access internaly, if you are using above configuration you can set X-File as:

response['X-File'] = '/files/filename.extension'

By doing this with nginx configuration, the file will be protected & also you can control the file from django views

寄人书 2024-08-03 23:25:57

另一个值得一看的项目: http://readthedocs.org /docs/django-private-files/en/latest/usage.html
看起来很有希望,但我自己还没有测试过。

基本上,该项目抽象了 mod_xsendfile 配置并允许您执行以下操作:

from django.db import models
from django.contrib.auth.models import User
from private_files import PrivateFileField

def is_owner(request, instance):
    return (not request.user.is_anonymous()) and request.user.is_authenticated and
                   instance.owner.pk = request.user.pk

class FileSubmission(models.Model):
    description = models.CharField("description", max_length = 200)
        owner = models.ForeignKey(User)
    uploaded_file = PrivateFileField("file", upload_to = 'uploads', condition = is_owner)

Another project to have a look at: http://readthedocs.org/docs/django-private-files/en/latest/usage.html
Looks promissing, haven't tested it myself yet tho.

Basically the project abstracts the mod_xsendfile configuration and allows you to do things like:

from django.db import models
from django.contrib.auth.models import User
from private_files import PrivateFileField

def is_owner(request, instance):
    return (not request.user.is_anonymous()) and request.user.is_authenticated and
                   instance.owner.pk = request.user.pk

class FileSubmission(models.Model):
    description = models.CharField("description", max_length = 200)
        owner = models.ForeignKey(User)
    uploaded_file = PrivateFileField("file", upload_to = 'uploads', condition = is_owner)
提笔书几行 2024-08-03 23:25:57

Django 建议您使用另一台服务器来提供静态媒体(在同一台计算机上运行另一台服务器就可以了。)他们建议使用诸如 lighttp

设置起来非常简单。 然而。 如果“somefile.txt”是根据请求生成的(内容是动态的),那么您可能希望 django 为其提供服务。

Django 文档 - 静态文件

Django recommend that you use another server to serve static media (another server running on the same machine is fine.) They recommend the use of such servers as lighttp.

This is very simple to set up. However. if 'somefile.txt' is generated on request (content is dynamic) then you may want django to serve it.

Django Docs - Static Files

寻找我们的幸福 2024-08-03 23:25:57

我不止一次遇到同样的问题,因此使用 xsendfile 模块和 auth 视图装饰器实现 django-filelibrary。 请随意使用它作为您自己的解决方案的灵感。

https://github.com/danielsokolowski/django-filelibrary

I have faced the same problem more then once and so implemented using xsendfile module and auth view decorators the django-filelibrary. Feel free to use it as inspiration for your own solution.

https://github.com/danielsokolowski/django-filelibrary

遗失的美好 2024-08-03 23:25:57

对于非常简单但效率不高或可扩展的解决方案,您可以仅使用内置的 django serve 视图。 这对于快速原型或一次性工作非常有用,但正如本问题中所提到的,您应该在生产中使用 apache 或 nginx 之类的东西。

from django.views.static import serve
filepath = '/some/path/to/local/file.txt'
return serve(request, os.path.basename(filepath), os.path.dirname(filepath))

For a very simple but not efficient or scalable solution, you can just use the built in django serve view. This is excellent for quick prototypes or one-off work, but as has been mentioned throughout this question, you should use something like apache or nginx in production.

from django.views.static import serve
filepath = '/some/path/to/local/file.txt'
return serve(request, os.path.basename(filepath), os.path.dirname(filepath))
旧人哭 2024-08-03 23:25:57

“下载”只是 HTTP 标头的更改。

请参阅 http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment 了解如何响应下载。

您只需要为 "/download" 定义一个 URL。

请求的 GETPOST 字典将包含 "f=somefile.txt" 信息。

您的视图函数将简单地将基本路径与“f”值合并,打开文件,创建并返回响应对象。 它应该少于 12 行代码。

A "download" is simply an HTTP header change.

See http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment for how to respond with a download.

You only need one URL definition for "/download".

The request's GET or POST dictionary will have the "f=somefile.txt" information.

Your view function will simply merge the base path with the "f" value, open the file, create and return a response object. It should be less than 12 lines of code.

ぽ尐不点ル 2024-08-03 23:25:57

S.Lott 拥有“好”/简单的解决方案,elo80ka 拥有“最佳”/高效的解决方案。 这是一个“更好”/中间的解决方案 - 无需服务器设置,但对于大文件比天真的修复更有效:

http://djangosnippets.org/snippets/365/

基本上,Django 仍然处理文件服务,但不会立即将整个文件加载到内存中。 这允许您的服务器(缓慢地)提供大文件,而不会增加内存使用量。

同样,S.Lott 的 X-SendFile 对于较大的文件来说仍然更好。 但是,如果您不能或不想为此烦恼,那么这种中间解决方案将为您带来更高的效率,而无需麻烦。

S.Lott has the "good"/simple solution, and elo80ka has the "best"/efficient solution. Here is a "better"/middle solution - no server setup, but more efficient for large files than the naive fix:

http://djangosnippets.org/snippets/365/

Basically, Django still handles serving the file but does not load the whole thing into memory at once. This allows your server to (slowly) serve a big file without ramping up the memory usage.

Again, S.Lott's X-SendFile is still better for larger files. But if you can't or don't want to bother with that, then this middle solution will gain you better efficiency without the hassle.

如何视而不见 2024-08-03 23:25:57

只需提及 Django 1.10 中可用的 FileResponse 对象

编辑:在寻找通过 Django 流式传输文件的简单方法时刚刚遇到了我自己的答案,所以这里是一个更完整的示例(给未来的我)。 假设 FileField 名称为 imported_file

views.py

from django.views.generic.detail import DetailView   
from django.http import FileResponse
class BaseFileDownloadView(DetailView):
  def get(self, request, *args, **kwargs):
    filename=self.kwargs.get('filename', None)
    if filename is None:
      raise ValueError("Found empty filename")
    some_file = self.model.objects.get(imported_file=filename)
    response = FileResponse(some_file.imported_file, content_type="text/csv")
    # https://docs.djangoproject.com/en/1.11/howto/outputting-csv/#streaming-large-csv-files
    response['Content-Disposition'] = 'attachment; filename="%s"'%filename
    return response

class SomeFileDownloadView(BaseFileDownloadView):
    model = SomeModel

urls.py

...
url(r'^somefile/(?P<filename>[-\w_\\-\\.]+)
, views.SomeFileDownloadView.as_view(), name='somefile-download'),
...

Just mentioning the FileResponse object available in Django 1.10

Edit: Just ran into my own answer while searching for an easy way to stream files via Django, so here is a more complete example (to future me). It assumes that the FileField name is imported_file

views.py

from django.views.generic.detail import DetailView   
from django.http import FileResponse
class BaseFileDownloadView(DetailView):
  def get(self, request, *args, **kwargs):
    filename=self.kwargs.get('filename', None)
    if filename is None:
      raise ValueError("Found empty filename")
    some_file = self.model.objects.get(imported_file=filename)
    response = FileResponse(some_file.imported_file, content_type="text/csv")
    # https://docs.djangoproject.com/en/1.11/howto/outputting-csv/#streaming-large-csv-files
    response['Content-Disposition'] = 'attachment; filename="%s"'%filename
    return response

class SomeFileDownloadView(BaseFileDownloadView):
    model = SomeModel

urls.py

...
url(r'^somefile/(?P<filename>[-\w_\\-\\.]+)
, views.SomeFileDownloadView.as_view(), name='somefile-download'),
...
暮年 2024-08-03 23:25:57

尝试过 @Rocketmonkeys 解决方案,但下载的文件被存储为 *.bin 并给出随机名称。 这当然不好。 添加来自 @elo80ka 的另一行解决了问题。
这是我现在使用的代码:

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

filename = "/home/stackoverflow-addict/private-folder(not-porn)/image.jpg"
wrapper = FileWrapper(file(filename))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(filename)
response['Content-Length'] = os.path.getsize(filename)
return response

您现在可以将文件存储在私有目录中(不在 /media 或 /public_html 内),并通过 django 将它们公开给某些用户或在某些情况下。
希望能帮助到你。

感谢@elo80ka、@S.Lott 和 @Rocketmonkeys 的解答,得到了结合所有这些的完美解决方案 =)

Tried @Rocketmonkeys solution but downloaded files were being stored as *.bin and given random names. That's not fine of course. Adding another line from @elo80ka solved the problem.
Here is the code I'm using now:

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

filename = "/home/stackoverflow-addict/private-folder(not-porn)/image.jpg"
wrapper = FileWrapper(file(filename))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(filename)
response['Content-Length'] = os.path.getsize(filename)
return response

You can now store files in a private directory (not inside /media nor /public_html) and expose them via django to certain users or under certain circumstances.
Hope it helps.

Thanks to @elo80ka, @S.Lott and @Rocketmonkeys for the answers, got the perfect solution combining all of them =)

拥抱我好吗 2024-08-03 23:25:56

为了实现“两全其美”,您可以将 S.Lott 的解决方案与 xsendfile 模块:django 生成文件的路径(或文件本身) ,但实际的文件服务是由 Apache/Lighttpd 处理的。 设置 mod_xsendfile 后,与视图集成需要几行代码:

from django.utils.encoding import smart_str

response = HttpResponse(mimetype='application/force-download') # mimetype is replaced by content_type for django 1.7
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
# It's usually a good idea to set the 'Content-Length' header too.
# You can also set any other required headers: Cache-Control, etc.
return response

当然,只有当您可以控制服务器,或者您的托管公司已经设置了 mod_xsendfile 时,这才有效。

编辑:

django 1.7 中 mimetype 被 content_type 取代

response = HttpResponse(content_type='application/force-download')  

编辑:
对于 nginx 检查这个,它使用 X-Accel-Redirect 而不是 apache X-Sendfile 标头。

For the "best of both worlds" you could combine S.Lott's solution with the xsendfile module: django generates the path to the file (or the file itself), but the actual file serving is handled by Apache/Lighttpd. Once you've set up mod_xsendfile, integrating with your view takes a few lines of code:

from django.utils.encoding import smart_str

response = HttpResponse(mimetype='application/force-download') # mimetype is replaced by content_type for django 1.7
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
# It's usually a good idea to set the 'Content-Length' header too.
# You can also set any other required headers: Cache-Control, etc.
return response

Of course, this will only work if you have control over your server, or your hosting company has mod_xsendfile already set up.

EDIT:

mimetype is replaced by content_type for django 1.7

response = HttpResponse(content_type='application/force-download')  

EDIT:
For nginx check this, it uses X-Accel-Redirect instead of apache X-Sendfile header.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文