Я кодировал небольшое приложение Flask для загрузки файлов с Google Drive.
@app.route("/downloadFile/<id>")
def downloadFile(id):
ioBytes, name, mime = gdrive.downloadFile(id)
return send_file(ioBytes, mime, True, name)
Я использовал метод загрузки из примера здесь , с небольшими изменениями
def downloadFile(self, file_id):
file = self.drive.files().get(fileId=file_id).execute()
request = self.drive.files().get_media(fileId=file_id)
fh = io.BytesIO()
downloader = MediaIoBaseDownload(fh, request)
done = False
while done is False:
status, done = downloader.next_chunk()
print("Downloading {} - {}%".format(file.get('name'), int(status.progress() * 100)))
fh.seek(0)
return (fh, file.get('name'), file.get('mimeType'))
Он работал как положено и загрузил файл на мой компьютер.
Теперь я хочу развернуть это приложение Flask на Heroku. Моя проблема связана с тайм-аутами HTTP, как указано здесь :
У HTTP-запросов есть начальное 30-секундное окно, в котором веб-процесс должен возвращать данные ответа
Поскольку загрузка некоторых из моих файлов может занять более 30 секунд, это становится большой проблемой.
Я пытался использовать класс Response и оператор yield для продолжения отправки пустых байтов до Я скачал и отправил файл со следующей функцией:
def sendUntilEndOfRequest(func, args=()):
def thread():
with app.app_context(), app.test_request_context():
return func(*args)
with concurrent.futures.ThreadPoolExecutor() as executor:
ret = ""
def exec():
while ret == "":
yield ""
time.sleep(1)
yield ret
future = executor.submit(thread)
def getValue():
nonlocal ret
ret = future.result()
threading.Thread(target=getValue).start()
return Response(stream_with_context(exec()))
Я попытался сделать его несколько обобщенным c, чтобы, если у меня есть какая-либо другая функция, выполнение которой занимает более 30 секунд, я могу используйте его.
Теперь мой код загрузки
@app.route("/downloadFile/<id>")
def downloadFile(id):
def downloadAndSendFile():
ioBytes, name, mime = gdrive.downloadFile(id)
return send_file(ioBytes, mime, True, name)
return sendUntilEndOfRequest(downloadAndSendFile)
Но каждый раз, когда я пытаюсь запустить этот код, он выдает эту ошибку:
127.0.0.1 - - [15/Jan/2020 20:38:06] "[37mGET /downloadFile/1heeoEBZrhW0crgDSLbhLpcyMfvXqSmqi HTTP/1.1[0m" 200 -
Error on request:
Traceback (most recent call last):
File "C:\Users\fsvic\AppData\Local\Programs\Python\Python37\lib\site-packages\werkzeug\serving.py", line 303, in run_wsgi
execute(self.server.app)
File "C:\Users\fsvic\AppData\Local\Programs\Python\Python37\lib\site-packages\werkzeug\serving.py", line 294, in execute
write(data)
File "C:\Users\fsvic\AppData\Local\Programs\Python\Python37\lib\site-packages\werkzeug\serving.py", line 274, in write
assert isinstance(data, bytes), "applications must write bytes"
AssertionError: applications must write bytes
Видимо, файл загружается правильно. Я проверил замену send_file
на команду render_template
, чтобы проверить, возможно ли получение объектов flask, и это работало отлично. Я также протестировал возвращаемые строки, и это также сработало.
В конце концов, как я могу получить загруженный файл?