我有一个Django APIView,它生成CSV文件流并使用StreamingHttpResponse返回它。我也希望在单独的线程中将生成的文件也上传到s3,因为我不希望响应停止直到上传完成。
我想到将流保存在TemporaryFile对象中,并在流完成后使用它在单独的线程中触发s3上传。使用这种方法时,由于上下文管理器退出时文件自动关闭,因此无法使用上下文管理器。上传完成后,我不得不求助于手动关闭文件。我想避免此手动文件关闭过程。我该如何解决?
这是视图:
class CsvExportView(APIView):
def get(self, request):
response = StreamingHttpResponse(
streaming_content=get_file_stream(),
content_type='text/csv'
)
response['Content-Disposition'] = 'attachment; filename="{}"'.format('some_name')
return response
Here is the get_file_stream()
method:
import threading
from tempfile import TemporaryFile
class TempFileManager(object):
def __init__(self):
self._file = TemporaryFile()
def get_file(self, parameter_list):
pass
def write_to_file(self, data)
self._file.write(data)
def close_file(self):
self._file.close()
def get_file_stream():
file_mng = TempFileManager()
# get_csv_stream method is a generator that yields csv file content
# by calling external api and complex data processing
for data in get_csv_stream():
# keep writing to temp file
file_mng.write_to_file(data)
# yield chunks to browser
yield data
# once all data is received
# spawn a thread and do the upload
# complete request lifecycle
t = threading.Thread(target=upload_file_to_s3, args=(file_mng, ))
t.start()
def upload_file_to_s3(self, manager):
# some code to upload file
upload(manager.get_file())
# remember to close file
manager.close_file()
上面的方法很好用,但是看起来很混乱并且容易出错。如果像这样使用上下文管理器,这会容易得多:
def get_file_stream():
with TemporaryFile() as tmp:
for data in get_csv_stream():
# keep writing to temp file
tmp.write(data)
yield data
upload_file_to_s3(tmp)
但是我不希望用户等到文件上传到s3之前,所以由于涉及线程,上下文管理器甚至在上传过程开始之前就可能关闭文件。
有谁知道更好的方法来处理这种情况?