Как очистить временный файл, используемый с send_file?
В настоящее время я разрабатываю интерфейс 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)
В настоящее время это не работает, потому что когда я делаю очистку, колба находится в процессе чтения файла и отправки его клиенту.
¨
Как я могу это решить?
Изменить: я забыл упомянуть, что файлы находятся во временных каталогах.
Ответы
Ответ 1
Если вы используете 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), чтобы запускать каждый час или около того и удалять файлы во временном каталоге, которые были созданы более чем за полчаса до этого.
Ответ 2
У меня есть два решения.
Первое решение - удалить файл в методе __exit__
, но не закрыть его. Таким образом, файл-объект все еще доступен, и вы можете передать его в send_file
.
Это будет работать, только если вы не используете X-Sendfile
, потому что он использует имя файла.
Второе решение - полагаться на сборщик мусора. Вы можете передать send_file
файл-объект, который очистит файл при удалении (метод __del__
). Таким образом, файл удаляется только тогда, когда файл-объект удаляется из python. Вы можете использовать TemporaryFile
для этого, если вы этого еще не сделали.
Ответ 3
Метод, который я использовал, - использовать слабые ссылки для удаления файла после завершения ответа.
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, которые я использовал.
Ответ 4
Немного поздно, но это то, что я использовал с помощью предложений madjar (в случае, если кто-то еще сталкивается с этим). Это небольшая вспомогательная функция, которую я использую (в качестве параметра используется объект 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