drf 文件下载

861 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

这是一篇小基础文,日常开发中,肯定或多或少都会遇到过要提供数据文件下载的,特别是做一些关于数据统计啥需求的时候, 这个基本是必须的要求。

然后对于在django中,也有对应的返回处理方法,官方文档的话可以参考一下这文章:docs.djangoproject.com/en/1.11/ref…

然后我在这也简单的浅聊一下下面这几种返回文件下载的方式:

  1. HttpResponse
  2. StreamingHttpResponse
  3. FileResponse

HttpResponse

在django中,开发一个接口,HttpResponse常用于返回数据,那么其实对于返回文件也是可以的。

后端接口返回文件的,大概流程是先把文件读取为流,然后通过设置response中的Content-Type,Content-Disposition, 然后请求对应接口,就能把文件下载到本地。

对于HttpResponse 也是这样子的大概流程,对应demo代码

# 设置路由
path('api/download/',views.download,name="download"),

from django.shortcuts import HttpResponse

def download(request):
  # 读取文件
  file = open('test.xls', 'rb')
  response = HttpResponse(file)
  # 以流的形式下载文件,这样可以实现任意格式的文件下载
  response['Content-Type'] = 'application/octet-stream'
  # Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名
  response['Content-Disposition'] = 'attachment;filename="test.xls"'
  return response

当类似请求127.0.0.1:8000/api/download/ 的时候就会发现本地下载了一个test.xls文件

不过HttpResponse有个弊端,其工作原理是先读取文件,载入内存,然后再输出。 如果下载文件很大,该方法会占用很多内存。

对于下载大文件,Django更推荐StreamingHttpResponse和FileResponse方法, 这两个方法将下载文件分批(Chunks)写入用户本地磁盘,先不将它们载入服务器内存。

StreamingHttpResponse

StreamingHttpResponse(streaming_content):流式相应,内容的迭代器形式,以内容流的方式响应。 StreamingHttpResponse是将文件内容进行流式传输,数据量大可以用这个方法。

其实代码上,StreamingHttpResponse使用和HttpResponse流程中并没有太多的差异, 只是在demo中,把HttpResponse替换为StreamingHttpResponse,但是相比HttpResponse它又是有优势的。

def file_iterator(file_path, chunk_size=512):
    """
    文件读取迭代器
    :return:
    """
    with open(file_path, 'rb') as target_file:
        while True:
            chunk = target_file.read(chunk_size)
            if chunk:
                yield chunk
            else:
                break

from django.http import StreamingHttpResponse

def download(request):
  # 读取文件
  file = 'test.xls'
  response = StreamingHttpResponse(file_iterator(file))
  # 以流的形式下载文件,这样可以实现任意格式的文件下载
  response['Content-Type'] = 'application/octet-stream'
  # Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名
  response['Content-Disposition'] = 'attachment;filename="test.xls"'
  return response

FileResponse

FileResponse方法是SteamingHttpResponse的子类,内部使用迭代器进行数据流传输,也是最推荐使用它来作为文件下载的。

from django.http import FileResponse

def download(request):
  # 读取文件
  file = 'test.xls'
  response = FileResponse(file_iterator(file))
  # 以流的形式下载文件,这样可以实现任意格式的文件下载
  response['Content-Type'] = 'application/octet-stream'
  # Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名
  response['Content-Disposition'] = 'attachment;filename="test.xls"'
  return response

注意

毕竟是chinese,一般文件的命名肯定会很带中国特色的语言,汉语(中文),如果你设置的Content-Disposition 是这样子的:

response['Content-Disposition'] = 'attachment;filename="test.xls"'

请求接口下载的时候问题不大,下载下来的就是test.xls文件。但是如果你是这样子:

response['Content-Disposition'] = 'attachment;filename="测试.xls"'

你也许会发现下载下来的文件和你预期的文件并不一样,文件名也没有,啥都没了(windows上面测试的,Mac有条件的也可试试)

所以在设置filename的时候,最好加上escape_uri_path,它解决文件下载中文命名出现乱码的情况,类似这样子:

response['Content-Disposition'] = 'attachment;filename="{}"'.format(escape_uri_path('测试.xls'))

这时候你再去尝试请求下载,你会发现能正常下载测试.xls文件了。

结语

截止到这里,该讲的已经讲完了,也希望这些东西能给人一点点作用,当然,文章不是完美的,有缺漏有错的也可以提出来,我也能进一步学习。

最后,如果可以,点赞支持一波也行。