一个将文件传输任务委托给下游代理的Django应用程序。
项目描述
一个SmartFile开源项目。 了解更多关于SmartFile如何使用和贡献开源软件的信息。
简介
Django项目建议从不同于执行Web应用程序的Web服务器上提供服务静态文件。当静态文件是Web资源时,这很容易实现。这些资源可以为任何匿名用户提供服务,并且可以轻松缓存。然而,在某些情况下,应用程序必须控制对文件的访问,甚至允许用户上传文件。在这些情况下,需要紧密控制过程,这与Django项目的建议相反。
幸运的是,有一些工具可以移除下载甚至上传,同时仍然允许应用程序控制这个过程。这个Django应用程序旨在帮助与这些工具集成,以便您的Web应用程序可以将文件传输委托给下游代理服务器,该服务器更适合处理这项任务,从而释放应用程序服务器用于重负载。
django-transfer集成了
mod_xsendfile用于Apache
X-Accel-Redirect用于Nginx
X-SendFile头部在Lighttpd
mod_upload 用于 Nginx
上述前三个允许网络应用程序发送一个头部信息,指示内容服务器将文件传输给 HTTP 客户端。这样,网络应用程序仍然接收下载请求,执行所需的任何检查,并发送一个头部信息而不是实际的文件内容。
最后一个,mod_upload 做了类似的事情,但针对的是文件上传。mod_upload 会接收发送到服务器的文件,并将它们保存在临时文件中。然后它将请求转发给网络应用程序,用包含这些文件的临时文件路径替换文件正文。
mod_upload 比简单地缓冲上传更好,因为文件正文永远不会由应用程序服务器处理。实际上,如果您可以将临时文件写入与最终位置相同的卷上的保留区域,完成上传只需要简单的移动操作。实际上,ProxyUploadedFile 类(包含在 request.FILES 中)有一个方便的 move() 方法。
下载
django-transfer 提供了一个 HttpResponse 子类,用于处理由响应头部触发的下载。实际的头部和格式由该类处理。TransferHttpResponse 接受一个路径,并处理传输。当 settings.DEBUG == True 时,路径将直接发送到客户端,这使得 Django 开发服务器可以在不更改您的应用程序代码的情况下正常工作。
下载的事件时间线如下。
客户端启动下载(GET 请求)。
下游服务器将请求转发到 Django。
Django 应用程序验证用户并进行其他必要的处理。
Django 应用程序返回一个 TransferHttpResponse。
TransferHttpResponse 发出一个头部信息,指示下游服务器将文件传输给客户端。
首先,您必须配置 django-transfer 并让它了解有关您的下游服务器的详细信息。
服务器类型
TRANSFER_SERVER = 'apache' # or 'nginx' or 'lighttpd'
您可以更改服务器类型,并且 TransferHttpResponse 将使用配置的服务器的正确头部信息。
Nginx 映射
Nginx 内置了对 X-Accel-Redirect 头部的支持。然而,它不接受任意路径进行传输。Nginx 需要您配置内部位置,并返回一个相对于这些位置的路径。
例如,如果您配置
location /downloads { internal; alias /mnt/shared/downloads; }
当 Nginx 接收到头部信息 X-Accel-Redirect: /downloads/foo/bar.png 时,它将 '/mnt/shared/downloads/foo/bar.png' 传输给客户端。
django-transfer 需要知道这样的位置。您可以通过配置映射来通知它。
TRANSFER_MAPPINGS = { '/mnt/shared/downloads': '/downloads', }
一旦配置了映射,您就可以使用绝对路径,这些路径将被转换为 Nginx 所需的位置。如果您稍后切换到不同的服务器(Apache 或 lighttpd),这些绝对路径将继续工作而无需更改您的代码。同样,当 settings.DEBUG == True 时,需要绝对路径,以便开发服务器可以直接发送文件。
如果您没有配置任何映射,并且您正在使用服务器类型 'nginx',将引发 ImproperlyConfigured 异常。当服务器类型不是 'nginx' 时,忽略映射。
Apache 配置
Apache 需要安装一个模块才能使用 X-Sendfile 头部。一旦安装,此模块必须被启用,并且您必须定义允许下载的位置。与 Nginx 类似,Apache 不会服务任意路径,只有那些特定配置的路径。
XSendFile On XSendFilePath /mnt/shared/downloads
当Apache接收到头部 X-SendFile: /mnt/shared/downloads/foo/bar.png 时,它将 '/mnt/shared/downloads/foo/bar.png' 传输给客户端。当服务器类型为 'apache' 时,django-transfer会传递绝对路径。
Lighttpd 配置
待办:我从未使用过lighttpd,但我知道它支持这个。
上传
上传处理使用类似(但相反)的过程。Nginx支持使用 mod_upload 进行上传。这不是默认服务器的一部分,因此您必须构建支持上传的nginx。如果可用,上传模块将从POST请求中删除文件内容,将它们保存到临时文件中,然后将这些文件名转发给您的应用程序。
客户端启动上传(POST请求)。
下游服务器将任何文件保存到暂存区。
下游服务器将请求(除文件内容外)转发给Django。
Django进行必要的处理并返回响应。
下游服务器将响应转达到客户端。
要像处理常规文件上传一样处理下游上传,您必须安装 TransferMiddleware。此中间件处理 request.POST 数据,识别上传文件,并在 request.FILES 中创建新条目以表示它们。
MIDDLEWARE_CLASSES = ( ... 'django_transfer.TransferMiddleware', ... )
Nginx需要一些配置才能实现这一点。下面是一个示例配置。
location /upload { upload_pass @application; # The path below must exist, so must subdirectories named 0-9 # $ mkdir -p /mnt/shared/uploads/{0-9} upload_store /mnt/shared/uploads 1; upload_store_access user:r; # You can limit file size here... upload_max_file_size 0; # These are the MINIMUM fields required by django-transfer. # mod_upload will replace $upload_field_name with the name of the file # field. If there are multiple files, your web application will receive # a set of filename/paths for each. upload_set_form_field $upload_field_name[filename] "$upload_file_name"; upload_set_form_field $upload_field_name[path] "$upload_tmp_path"; # You can also pass along the following fields, otherwise # django-transfer will attempt to "figure out" these values on it's # own. upload_set_form_field $upload_field_name[content_type] "$upload_content_type"; upload_aggregate_form_field $upload_field_name[size] "$upload_file_size"; # If you want to receive non-file fields provide the following, note # that if nginx supports it, this can be a regular expression. If not # you can define allowed fields separately, by providing this argument # multiple times. upload_pass_form_field ".*"; # If you want to receive querystring arguments... upload_pass_args on; } location / { # ... proxy-pass or FCGI directives here ... # This is where requests to URLs other than /upload go. } location @application { # ... proxy-pass or FCGI directives here ... # This is where to pass upload requests, most frequently, it will be # the same as the previous location. }
有关如何安装和配置mod_upload的更多信息,请参阅以下页面,我在实现此功能时发现它们很有用。
http://www.grid.net.ru/nginx/upload.en.html http://blog.joshsoftware.com/2010/10/20/uploading-multiple-files-with-nginx-upload-module-and-upload-progress-bar/ http://bclennox.com/extremely-large-file-uploads-with-nginx-passenger-rails-and-jquery
您的视图现在可以以相同的方式处理常规或下游上传。
开发 / 调试
当 settings.DEBUG == True 时,TransferHttpResponse 将直接传输文件,这对于与Django开发服务器一起使用是合适的。《TransferMiddleware》始终支持常规文件上传,因此当 settings.DEBUG == True 时,它也能正常工作。
非ASCII文件名
此库不会对非ASCII文件名提供任何帮助,但是关于这个主题的简要说明可能会节省您一些麻烦。
一种常见的做法是包含一个包含文件名的Content-Disposition头部。当文件名包含非ASCII字符(UTF-8等)时,这会中断。具体来说,当您尝试设置头部时,Django将引发异常。HTTP规范指出,头部必须只包含ASCII字符。
我找到的最佳解决方案是将文件名包含在URL中。它必须是URL的最后一个元素。我知道的所有浏览器都将使用此文件名在“另存为”对话框中。由于URL可以包含任何字符,这解决了问题。为了实现这一点,我通常在urls.py中添加一个正则表达式,忽略文件名。文件名仅为了浏览器的好处,并且不会由Django视图使用。因此
url('^/download/.*', 'myapp.views.download'),
将允许我们为此目的有一个可选的尾随文件名。然后您必须确保任何指向您的下载视图的链接都包含文件名,如下所示
http://myapp.com/download/desired_filename.png
当用户点击该链接且应用程序发送文件内容时,浏览器将从URL中获取文件名。浏览器可能会决定渲染或保存文件。您可以通过包含值为“attachment;”的Content-Disposition头信息来强制(保存与渲染)问题,排除(不安全的)文件名。
项目详情
django-transfer-0.4.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 75ecd9ef287493914e51d9828acacd8737e5e3cae32b24ef46cb7495ebda9d5d |
|
MD5 | fc908ccfc1ee4aaf8130b7ca0337fdf9 |
|
BLAKE2b-256 | 4f85086ea0e40853e475da468751940abd6f1fa9e95eb026095043b2b05ae7ca |