
问题来源:
今天在写文件下载接口的时候,使用django的FileResponse对象对文件进行数据的转换
使用FileResponse对象的时候需要先打开文件对象
然后在return response之前,使用f.close()关闭了文件
经过调试发现,文件已经被关闭,不能正常return response
代码报错如下:ValueError: read of closed file
ValueError: read of closed file
由此,导致返回数据失败。前端拿不到正常的文件内容。
其实Djnago提供了三种方式实现文件下载功能,分别是:HttpResponse、StreamingHttpResponse和FileResponse,三者的说明如下:
HttpResponse 是所有响应过程的核心类,它的底层功能类是HttpResponseBase。
StreamingHttpResponse 是在HttpResponseBase 的基础上进行继承与重写的,它实现流式响应输出(流式响应输出是使用Python的迭代器将数据进行分段处理并传输的),适用于大规模数据响应和文件传输响应。
FileResponse 是在StreamingHttpResponse 的基础上进行继承与重写的,它实现文件的流式响应输出,只适用于文件传输响应。
HttpResponse、StreamingHttpResponse和FileResponse 这三者的差异:
HttpResponse 实现文件下载存在很大弊端,其工作原理是将文件读取并载入内存,然后输出到浏览器上实现下载功能。如果文件较大,该方法就会占用很多内存。对于下载大文件,Django推荐使用StreamingHttpResponse 和FileResponse 方法,这两个方法将下载文件分批写入服务器的本地磁盘,减少对内存的消耗。
StreamingHttpResponse 和FileResponse 的实现原理是相同的,两者都是将下载文件分批写入本地磁盘,实现文件的流式响应输出。
从适用范围来说,StreamingHttpResponse 的适用范围更为广泛,可支持大规模数据或文件输出,而FileResponse 只支持文件输出。
从使用方式来说,由于StreamingHttpResponse 支持数据或文件输出,因此在使用时需要设置响应输出类型和方式,而FileResponse只需设置3个参数即可实现文件下载功能。
但是对于上述三种方法, 都需要对文件进行读取操作,所以都可能会遇到这个问题,那么该如何解决呢?
为了让文件正常返回,不对文件进行主动关闭,发现可以下载成功。
因此,引发一个问题的思考,如果文件比较大,会不会因为被打开了,又没有主动关闭,从而占用内存,长时间运行导致内存溢出呢?
接下来将对该问题做出测试,并记录测试过程,得出结论。
问题测试过程:
下面将对这个问题做出实际测试:
先使用lsof命令查看文件的打开使用情况:

此时,文件并没有被打开
然后在下载函数中,加一个强制休眠
time.sleep(300)
然后重启django后,重新调用下载文件接口
后端处理卡住的时候,重新查看文件的占用情况
确定文件被占用,截图如下

等待休眠时间完成
重新查看文件的占用情况
发现文件已经没有被占用了
完整过程截图:

所以,得到结论是