1. 直接返回文件
如果静态文件在工程根目录的 media/test.zip,需要先将文件读入内存,再进行传输。代码如下:
settings.py 配置
1
2
| PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media/')
|
yourapp/views.py
1
2
3
4
5
6
7
8
9
10
11
12
| from django.conf import settings
from django.http import HttpResponse
from django.core.files.storage import FileSystemStorage
def download_file_direct_from_file(request):
file_system = FileSystemStorage(settings.MEDIA_ROOT)
file_name = 'test.zip'
with file_system.open(file_name) as f:
response = HttpResponse(f)
response['Content-Type'] = 'application/%s' % file_name.split('.')[-1]
response['Content-Disposition'] = 'attachment; filename="%s"'% file_name
return response
|
如果是从 API 调用,第三方获取的文件,那么只需要将 with file_system.open(file_name) as f
中的变量,f,赋值为请求到的文件体即可,如:f = requests.get(url).content。下面的代码示例,就不一一说明了,仅以下载本地文件为例。
2. 返回流式的文件
Django 的 HttpResponse 对象支持以迭代器作为初始化参数,将文件对象替换为一个迭代器或生成器,可以优化 Django 对大文件的处理。同时,Django 提供了 StreamingHttpResponse 对象取代 HttpResponse 对象,以支持以流的形式发送文件给浏览器。
可以自己实现一个迭代器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| from django.conf import settings
from django.http import StreamingHttpResponse
def download_file_direct(request):
file_name = 'test.zip'
full_path_file_name = ''.join([settings.MEDIA_ROOT, file_name])
def file_read(full_path_name, chunk_size=512):
with open(full_path_name, 'rb') as f:
while True:
chunks = f.read(chunk_size)
if chunks:
yield chunks
else:
break
response = StreamingHttpResponse(file_read(full_path_file_name))
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename=%s' % (file_name)
return response
|
也可以使用 Django 提供的 FileWrapper 类来迭代器化文件对象。
1
2
3
4
5
6
7
8
9
10
11
12
| from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.core.servers.basehttp import FileWrapper
from django.http import StreamingHttpResponse
def download_file_chunk_from_file(request):
file_system = FileSystemStorage(settings.MEDIA_ROOT)
file_name = 'test.zip'
response = StreamingHttpResponse(FileWrapper(file_system.open(file_name, 'rb'), 512))
response['Content_Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment; filename=%s' % file_name
return response
|
3. 利用 sendfile 机制
Django 处理文件时,需要将文件内容读入到内存,再将内存中的内容发送给浏览器。Django 对文件的转发处理能力远比不上 Nginx。比较好的方法是,使用 Django 做权限判断,然后设置 Nginx 转发文件。
sendfile 是一种高性能网络 IO 的方式,利用操作系统内核的 sendfile 调用,可以将文件内容直接推送到网卡的 buffer,避免了应用层读写文件的开销。而在 Nginx 中,可以通过 X-Accel-Redirect 的特性来实现 sendfile 机制的文件处理。
Nginx 配置
1
2
3
4
| location /media {
internal;
alias /var/www/my_django_project_root/media;
}
|
views.py
1
2
3
4
5
6
7
8
9
| from django.conf import settings
from django.http import HttpResponse
def download_file_from_nginx(request):
file_name = 'test.zip'
response['Content_Type']='application/octet-stream'
response['Content-Disposition'] = 'attachment;filename=%s' % (file_name)
response['X-Accel-Redirect'] = '/media/%s' % file_name
return response
|
4. 参考