我希望网站上的用户能够下载路径被遮蔽的文件,这样他们就不能直接下载。

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

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

有没有一种方法可以让Django为下载提供这个文件,而不是试图找到一个URL和视图来显示它?


当前回答

尝试@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的答案,得到了完美的解决方案=)

其他回答

“下载”只是一个HTTP报头更改。

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

“/download”只需要一个URL定义。

请求的GET或POST字典将有“f=somefile.txt”信息。

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

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

更新:在新版本的python提供者使用django FileResponse,如果可用,还增加了对许多服务器实现的支持,从lighthttp, caddy到hiawatha

使用

pip install django-fileprovider

将fileprovider app添加到INSTALLED_APPS设置中 添加fileprovider.middleware.FileProviderMiddleware到MIDDLEWARE_CLASSES设置中 在生产环境中将FILEPROVIDER_NAME设置为nginx或apache,开发时默认为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文件提供程序以一种你的代码只需要最小修改的方式实现。

Nginx配置

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

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

这里nginx在内部设置了一个url /files/只能访问的位置,如果你使用上述配置,你可以将X-File设置为:

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

通过在nginx配置中这样做,文件将受到保护&你也可以从django视图中控制文件

Django建议您使用另一台服务器来提供静态媒体(在同一台机器上运行另一台服务器是可以的)。他们建议使用lighttp这样的服务器。

设置起来非常简单。然而。如果'somefile.txt'是在请求时生成的(内容是动态的),那么你可能想让django来服务它。

Django Docs -静态文件

尝试@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的答案,得到了完美的解决方案=)

这里只提到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'),
...