我目前正在开发一个服务器端json接口,其中有几个临时文件在请求期间进行操作。
我在请求结束时清理这些文件的当前解决方案如下所示:
@app.route("/method",methods=['POST'])
def api_entry():
with ObjectThatCreatesTemporaryFiles() as object:
object.createTemporaryFiles()
return "blabalbal"
在这种情况下,清理会在对象.__ exit __()
中使用花边但是在某些情况下我需要将临时文件返回给客户端,在这种情况下代码如下所示:
@app.route("/method",methods=['POST'])
def api_entry():
with ObjectThatCreatesTemporaryFiles() as object:
object.createTemporaryFiles()
return send_file(object.somePath)
这当前不起作用,因为当我进行清理时,烧瓶正在读取文件并将其发送到客户端。 ¨ 我该如何解决这个问题?
编辑:我忘了提到文件位于临时目录中。
答案 0 :(得分:8)
如果您使用的是Flask 0.9或更高版本,则可以使用after_this_request
装饰器:
@app.route("/method",methods=['POST'])
def api_entry():
tempcreator = ObjectThatCreatesTemporaryFiles():
tempcreator.createTemporaryFiles()
@after_this_request
def cleanup(response):
tempcreator.__exit__()
return response
return send_file(tempcreator.somePath)
修改强>
由于这不起作用,您可以尝试使用cStringIO
(这假设您的文件足够小以适应内存):
@app.route("/method", methods=["POST"])
def api_entry():
file_data = dataObject.createFileData()
# Simplest `createFileData` method:
# return cStringIO.StringIO("some\ndata")
return send_file(file_data,
as_attachment=True,
mimetype="text/plain",
attachment_filename="somefile.txt")
或者,您可以像现在一样创建临时文件,但不依赖于您的应用程序来删除它们。相反,设置一个cron作业(或者如果你在Windows上运行一个计划任务)每小时左右运行一次,并删除临时目录中超过半小时创建的文件。
答案 1 :(得分:7)
我使用的方法是在响应完成后使用弱引用来删除文件。
import shutil
import tempfile
import weakref
class FileRemover(object):
def __init__(self):
self.weak_references = dict() # weak_ref -> filepath to remove
def cleanup_once_done(self, response, filepath):
wr = weakref.ref(response, self._do_cleanup)
self.weak_references[wr] = filepath
def _do_cleanup(self, wr):
filepath = self.weak_references[wr]
print('Deleting %s' % filepath)
shutil.rmtree(filepath, ignore_errors=True)
file_remover = FileRemover()
在烧瓶电话中,我有:
@app.route('/method')
def get_some_data_as_a_file():
tempdir = tempfile.mkdtemp()
filepath = make_the_data(dir_to_put_file_in=tempdir)
resp = send_file(filepath)
file_remover.cleanup_once_done(resp, tempdir)
return resp
这是非常通用的,因为我已经使用了三种不同的python web框架。
答案 2 :(得分:3)
我有两个解决方案。
第一种解决方案是删除__exit__
方法中的文件,但不关闭它。这样,文件对象仍然可以访问,您可以将其传递给send_file
。
这仅在您不使用X-Sendfile
时才有效,因为它使用文件名。
第二种解决方案是依靠垃圾收集器。您可以将send_file
文件对象传递给删除文件(__del__
方法)。这样,只有从python中删除文件对象时才会删除该文件。如果您还没有,可以使用TemporaryFile
。
答案 3 :(得分:2)
这有点晚了,但这是我使用madjar's建议所做的事情(如果有其他人遇到此事)。这是我使用的一个小辅助函数(它需要一个PyExcelerate Workbook对象作为参数),你可以适应你的情况。只需改变你创建/构建tempfile.TemporaryFile的方式就可以了!在Windows 8.1和Ubuntu 12.04上测试。
def xlsx_to_response(wb, filename):
f = tempfile.TemporaryFile()
wb._save(f)
f.seek(0)
response = send_file(f, as_attachment=True, attachment_filename=filename,
add_etags=False)
f.seek(0, os.SEEK_END)
size = f.tell()
f.seek(0)
response.headers.extend({
'Content-Length': size,
'Cache-Control': 'no-cache'
})
return response
答案 4 :(得分:0)
我尝试过弱引用、烧瓶内置装饰器,但没有任何效果。
在每个系统中都有效的唯一想法是使用 io.BytesIO 在内存中创建一个临时文件
import os
import io
import tempfile
from multiprocessing import Process
import flask
def background_job(callback):
task = Process(target=callback())
task.start()
def send_temp_file(file_path: str, temp_dir: tempfile.TemporaryDirectory, remove_dir_after_send=True):
with open(file_path, "rb") as f:
content = io.BytesIO(f.read())
response = flask.send_file(content,
as_attachment=True,
attachment_filename=os.path.split(file_path)[0])
if remove_dir_after_send:
background_job(temp_dir.cleanup)
return response
app = flask.Flask(__name__)
@app.route("/serve_file/", methods=["GET"])
def serve_file():
temp_dir = tempfile.TemporaryDirectory()
file_path = os.path.join(temp_dir.name, "test.txt")
with open(file_path, "w") as f:
f.write("Hello World!")
return send_temp_file(file_path, temp_dir)
if __name__ == "__main__":
app.run(port=1337)