一个将文件传输任务委托给下游代理的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 |