跳转到主要内容

一个将文件传输任务委托给下游代理的Django应用程序。

项目描述

Travis CI Status

一个SmartFile开源项目。 了解更多关于SmartFile如何使用和贡献开源软件的信息。

SmartFile

简介

Django项目建议从不同于执行Web应用程序的Web服务器上提供服务静态文件。当静态文件是Web资源时,这很容易实现。这些资源可以为任何匿名用户提供服务,并且可以轻松缓存。然而,在某些情况下,应用程序必须控制对文件的访问,甚至允许用户上传文件。在这些情况下,需要紧密控制过程,这与Django项目的建议相反。

幸运的是,有一些工具可以移除下载甚至上传,同时仍然允许应用程序控制这个过程。这个Django应用程序旨在帮助与这些工具集成,以便您的Web应用程序可以将文件传输委托给下游代理服务器,该服务器更适合处理这项任务,从而释放应用程序服务器用于重负载。

django-transfer集成了

上述前三个允许网络应用程序发送一个头部信息,指示内容服务器将文件传输给 HTTP 客户端。这样,网络应用程序仍然接收下载请求,执行所需的任何检查,并发送一个头部信息而不是实际的文件内容。

最后一个,mod_upload 做了类似的事情,但针对的是文件上传。mod_upload 会接收发送到服务器的文件,并将它们保存在临时文件中。然后它将请求转发给网络应用程序,用包含这些文件的临时文件路径替换文件正文。

mod_upload 比简单地缓冲上传更好,因为文件正文永远不会由应用程序服务器处理。实际上,如果您可以将临时文件写入与最终位置相同的卷上的保留区域,完成上传只需要简单的移动操作。实际上,ProxyUploadedFile 类(包含在 request.FILES 中)有一个方便的 move() 方法。

下载

django-transfer 提供了一个 HttpResponse 子类,用于处理由响应头部触发的下载。实际的头部和格式由该类处理。TransferHttpResponse 接受一个路径,并处理传输。当 settings.DEBUG == True 时,路径将直接发送到客户端,这使得 Django 开发服务器可以在不更改您的应用程序代码的情况下正常工作。

下载的事件时间线如下。

  1. 客户端启动下载(GET 请求)。

  2. 下游服务器将请求转发到 Django。

  3. Django 应用程序验证用户并进行其他必要的处理。

  4. Django 应用程序返回一个 TransferHttpResponse

  5. 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请求中删除文件内容,将它们保存到临时文件中,然后将这些文件名转发给您的应用程序。

  1. 客户端启动上传(POST请求)。

  2. 下游服务器将任何文件保存到暂存区。

  3. 下游服务器将请求(除文件内容外)转发给Django。

  4. Django进行必要的处理并返回响应。

  5. 下游服务器将响应转达到客户端。

要像处理常规文件上传一样处理下游上传,您必须安装 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 (16.2 kB 查看哈希值)

上传时间

由以下支持