需解决的问题
最近需要实现一个功能,涉及到以下一些难点:
- 后端需要从一个外部数据源获取数据,但这个外部数据源响应比较慢,而且只支持每次最多5000行的请求,多于5000行需要分批次请求。
- 需要获取的数据量很大,而且由于上面提到的最大行数的问题,需要分批次获取。如果一次性全部获取将会占用大量内存,客户端那边也需要等很久,前端在等待的期间没实现任何ui提示变化。
解决方案
- 使用 Django 的 StreamingHttpResponse 功能,使用 python iterator 的特性,分批获取并返回数据,前端根据返回的文件流逐步下载数据。
步骤
一、首先实现后端将 csv 文件流并逐步响应的操作
这里的生成器根据个人开发需求编写,这里只是写了一个简单的例子作为参考。
def _title_row_generator(title_list, row_generator):
"""Use for first yield the title then yield the row"""
yield title_list
for row in row_generator:
yield row
def streaming_export_csv(filename: str, title_list, row_generator):
"""
Export csv with given filename, titles(first row), and other rows using StreamingHttpResponse.
"""
pseudo_buffer = Echo()
writer = csv.writer(pseudo_buffer)
response = StreamingHttpResponse((writer.writerow(row) for row in _title_row_generator(title_list, row_generator)),
content_type='text/csv; charset=utf-8')
response['Content-Disposition'] = 'attachment; filename="%s.csv"' % filename
response['Cache-Control'] = 'no-cache'
return response
二、实现前端对流数据的下载
我本人对前端只是略知一二,但当时由于我们组的前端有别的事情在忙,所以我就帮他研究了下。主要问题是大部分的方法都没有充分利用 streaming response
的特性。基本都会等到数据完全接受才会开始下载,如果需要等很久的话用户会认为按了按钮什么都没发生,经过一天的尝试,以下是可行的办法:
function () {
const url = '/api/somedata/';
const post_data = {
"csrfmiddlewaretoken":