Flask-如何在响应中产生send_file

问题描述 投票:0回答:1

我编写了一个小的Flask应用程序,以从Google云端硬盘下载文件。

@app.route("/downloadFile/<id>")
def downloadFile(id):
    ioBytes, name, mime = gdrive.downloadFile(id)
    return send_file(ioBytes, mime, True, name)

[我使用示例here中的下载方法,进行了很小的更改

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超时,如here

所述

HTTP请求有一个最初的30秒窗口,Web进程必须在该窗口中返回响应数据

由于我的某些文件可能需要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()))

我试图使它具有某种通用性,以便如果我有其他需要花费超过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来检查是否可以产生烧瓶对象,并且它工作得很好。我还测试了返回的字符串,它也可以正常工作。

最后,如何获取下载的文件?

python flask google-drive-api response yield
1个回答
0
投票

所有MediaIoBaseDownload所做的都是调用文件处理程序的write方法。因此,您可以像这样实现自己的IO:

© www.soinside.com 2019 - 2024. All rights reserved.