无法将大文件上传到 GCP App Engine 中的 Python + Flask

2024-01-25

更新:(2020 年 5 月 18 日)解决方法在本文最后!

我正在尝试将大型 CSV 文件(30MB - 2GB)从浏览器上传到运行 Python 3.7 + Flask 的 GCP App Engine,然后将这些文件推送到 GCP 存储。这在使用大文件进行本地测试时效果很好,但在 GCP 上会立即出现错误,并显示“413 - 您的客户发出的请求太大" 如果文件大于大约 20MB。这个错误在上传时立即发生,甚至还没有到达我的自定义 Python 逻辑(我怀疑 App Engine 正在检查Content-Length标题)。经过大量 SO/博客研究后,我尝试了许多解决方案,但均无济于事。请注意,我使用的是基本/免费 App Engine 设置以及运行 Gunicorn 服务器的 F1 实例。

首先,我尝试设置app.config['MAX_CONTENT_LENGTH'] = 2147483648但这并没有改变任何事情(SO post https://stackoverflow.com/questions/38972562/best-way-to-upload-large-csv-files-using-python-flask)。我的应用程序在到达我的 Python 代码之前仍然抛出错误:

# main.py
    app.config['MAX_CONTENT_LENGTH'] = 2147483648   # 2GB limit

    @app.route('/', methods=['POST', 'GET'])
    def upload():
        # COULDN'T GET THIS FAR WITH A LARGE UPLOAD!!!
        if flask.request.method == 'POST':

            uploaded_file = flask.request.files.get('file')

            storage_client = storage.Client()
            storage_bucket = storage_client.get_bucket('my_uploads')

            blob = storage_bucket.blob(uploaded_file.filename)
            blob.upload_from_string(uploaded_file.read())

<!-- index.html -->
    <form method="POST" action='/upload' enctype="multipart/form-data">
        <input type="file" name="file">
    </form>

经过进一步研究,我改用分块上传Flask-Dropzone,希望我可以批量上传数据,然后将 CSV 文件附加/构建为存储 Blob:

# main.py
app = flask.Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 2147483648   # 2GB limit
dropzone = Dropzone(app)


@app.route('/', methods=['POST', 'GET'])
def upload():

    if flask.request.method == 'POST':

        uploaded_file = flask.request.files.get('file')

        storage_client = storage.Client()
        storage_bucket = storage_client.get_bucket('my_uploads')

        CHUNK_SIZE = 10485760  # 10MB
        blob = storage_bucket.blob(uploaded_file.filename, chunk_size=self.CHUNK_SIZE)

        # hoping for a create-if-not-exists then append thereafter
        blob.upload_from_string(uploaded_file.read())

JS/HTML 直接来自我在网上找到的一些示例:

    <script>
       Dropzone.options.myDropzone = {
       timeout: 300000,
       chunking: true,
       chunkSize: 10485760 };
    </script>
    ....
    <form method="POST" action='/upload' class="dropzone dz-clickable" 
      id="dropper" enctype="multipart/form-data">
    </form>

上面确实分块上传(我可以看到对 POST /upload 的重复调用),but,调用blob.upload_from_string(uploaded_file.read())只是不断用最后上传的块替换 blob 内容,而不是追加。即使我删除了这也不起作用chunk_size=self.CHUNK_SIZE范围。

接下来我看了写/tmp然后到存储,但文档说写入/tmp占用了我仅有的一点内存,而其他地方的文件系统是只读的,所以这些都不起作用。

是否有附加 API 或批准的方法来将大文件上传到 GCP App Engine 并推送/流式传输到存储?鉴于代码在我的本地服务器上运行(并且很高兴上传到 GCP 存储),我假设这是 App Engine 中需要解决的内置限制。


解决方案(2020 年 5 月 18 日)我能够使用 Flask-Dropzone 让 JavaScript 将上传内容分割成许多 10MB 的块,并将这些块一次一个地发送到 Python 服务器。在 Python 方面,我们会不断附加到 /tmp 中的文件以“构建”内容,直到所有块都进入。最后,在最后一个块上,我们将上传到 GCP Storage,然后删除 /tmp 文件。

@app.route('/upload', methods=['POST'])
def upload():

    uploaded_file = flask.request.files.get('file')

    tmp_file_path = '/tmp/' + uploaded_file.filename
    with open(tmp_file_path, 'a') as f:
        f.write(uploaded_file.read().decode("UTF8"))

    chunk_index = int(flask.request.form.get('dzchunkindex')) if (flask.request.form.get('dzchunkindex') is not None)  else 0
    chunk_count = int(flask.request.form.get('dztotalchunkcount')) if (flask.request.form.get('dztotalchunkcount') is not None)  else 1

    if (chunk_index == (chunk_count - 1)):
        print('Saving file to storage')
        storage_bucket = storage_client.get_bucket('prairi_uploads')
        blob = storage_bucket.blob(uploaded_file.filename) #CHUNK??

        blob.upload_from_filename(tmp_file_path, client=storage_client)
        print('Saved to Storage')

        print('Deleting temp file')
        os.remove(tmp_file_path)
<!-- index.html -->
        <script>
          Dropzone.options.myDropzone = {
          ... // configs
          timeout: 300000,
          chunking: true,
          chunkSize: 1000000
        };
        </script>

请注意,/tmp 与 RAM 共享资源,因此您至少需要与上传文件大小一样多的 RAM,再加上 Python 本身的更多 RAM(我必须使用 F4 实例)。我想有一个更好的解决方案可以写入块存储而不是 /tmp,但我还没有做到这一点。


答案是您无法在单个 HTTP 请求中上传或下载大于 32 MB 的文件。Source https://cloud.google.com/appengine/quotas

您需要重新设计服务以在多个 HTTP 请求中传输数据、使用预签名 URL 将数据直接传输到 Cloud Storage,或者选择不使用全局前端 (GFE) 的其他服务,例如 Compute Engine。这不包括 Cloud Functions、Cloud Run、App EngineFlexible 等服务。

如果您使用多个 HTTP 请求,则需要管理内存,因为所有临时文件都存储在内存中。这意味着当您接近 2 GB 的最大实例大小时,您将会遇到问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无法将大文件上传到 GCP App Engine 中的 Python + Flask 的相关文章

  • Spring RESTful控制器方法改进建议

    我是 Spring REST 和 Hibernate 的新手 也就是说 我尝试组合一个企业级控制器方法 我计划将其用作未来开发的模式 您认为可以通过哪些方法来改进 我确信有很多 RequestMapping value user metho
  • 从脚本内更改自动热键托盘图标

    如何从 Autohotkey 脚本中将托盘图标更改为 my ico 例如 当脚本暂停时 为此 我在托盘菜单中提出了自己的 暂停脚本 菜单项 SingleInstance ignore Menu Tray Tip AutoCase 0 11
  • IE9-11 检测变换样式:preserve-3d

    我为一个项目制作了一个 3d 类型的菜单 自然 IE 会引起问题 因为 IE10 即使 3d 变换工作 也不支持变换样式 preserve 3d 我尝试了解决方法 通过对 3d 菜单容器的每个子元素应用变换 但至少可以说 动画看起来很糟糕
  • 我们什么时候应该在 Django 中使用“db_index=True”?

    当我们应该定义db index True在模型字段上 我正在尝试优化应用程序并且我想了解更多信息db index 什么情况下我们应该使用它 文档说使用db index True在模型字段上用于加速查找 但在存储和内存方面略有缺点 我们应该使
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959
  • bash:gitolite:找不到命令

    我正在尝试使用 Gitolite 在 Gitlab 中创建一个新分支 我完成安装步骤 当我遇到 设置 gitolite 部分时 我遇到了麻烦 我跟着这个link http sitaramc github com gitolite setup
  • ASP.NET Core MVC 视图组件搜索路径

    在此处的文档中 https learn microsoft com en us aspnet core mvc views view components view aspnetcore 2 2 https learn microsoft
  • xsi:type 属性搞乱了 C# XML 反序列化

    我使用 XSD exe 根据 XML 架构 xsd 文件 自动生成 C 对象 我正在反序列化 OpenCover 输出 但其中一个部分类未正确生成 这是导致异常的行
  • 一些基本的 PHP 问题 [已关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我只是有一些基本的 php 问题来加深我对学习的理解 但我找不到简单的答案 我有一个 php ajax 应用程序 它生成 mysql
  • If else 在 Web 网格列中

    如何在 webgrid 列中添加条件 if else grid GetHtml tableStyle table table bordered columns grid Columns grid Column RealName Name g
  • View.post(),以及当Runnables被执行时

    我最初的问题是需要知道我的根的高度和宽度View这样我就可以进行程序化的布局更改 就我的目的而言 我不一定需要在onCreate 对于我来说 以编程方式添加我的孩子就足够了View根布局完成后 因此我很乐意使用onWindowFocusCh
  • Azure Functions 计时器触发器线程安全

    我想知道是否有人知道如果您在 Azure 函数上设置了 Cron 设置 如果其任务执行时间超过 5 分钟 则每 5 分钟运行一次 会发生什么情况 它备份吗 或者我应该实现一个锁定功能 以防止某些东西 例如在循环中 处理先前调用已经处理的数据
  • SimpleIoC - 在缓存中找不到类型:Windows.UI.Xaml.Controls.Frame

    第一次由 SimpleIoC 实例化我的 ViewModel 时 我遇到了以下错误 我相信我已经按应有的方式设置了容器 但由于某种原因 我仍然收到以下错误 任何想法或帮助将非常感激 Microsoft Practices ServiceLo
  • svn 强制迁移

    我正在考虑将我们的 svn 代码库迁移到 perforce 看看谷歌搜索结果 我确实找到了两个具有相同功能的工具 P4转换ftp ftp perforce com pub perforce tools p4convert docs inde
  • java'assert'和'if(){}else exit;'之间的区别

    java和java有什么区别assert and if else exit 我可以用吗if else exit代替assert 也许有点谷歌 您应该记住的主要事情是 if else 语句应该用于程序流程控制 而assert 关键字应该仅用于
  • R闪亮:使用闪亮的JS从数据表中获取信息

    我想读出所有列名称以及它们在数据表中显示的顺序 由于不同的原因 我无法使用 stateSave 等选项 我对 JS 没有什么把握 但我确信用它可以完成 所以我需要你帮助我 我尝试过类似的代码片段 datatable data callbac
  • 从 JavaScript 中的 OnClientClick 事件中阻止 C# 中的 asp:Button OnClick 事件?

    我有一个asp Button在我的网页上 它调用 JavaScript 函数和代码隐藏方法 后者进行调用以导航到另一个页面 在 JavaScript 函数中 我正在检查条件 如果不满足这个条件 我想中止导航 以便OnClick方法未被调用
  • 自定义字符串查询操作的 Linq to NHibernate 可扩展性?

    我希望能够在 NHibernate Linq 表达式中使用自定义字符串查询 举例来说 这只是一个例子 我希望能够选择包含属性的实体 该属性是特定字符串的字谜 var myEntities EntityRepository AllEntiti
  • Keystore getEntry 在 Android 9 上返回 NULL

    c我已对存储在 Android 密钥库中的登录密码进行了加密和解密 在 Android 9 上 我观察到应用程序在尝试解密密码时崩溃 我无法重现它 但拥有 Pixel 3 的用户是崩溃的设备之一 下面是我如何从密钥库解密密码 private
  • Java 和/C++ 在多线程方面的差异

    我读过一些提示 多线程实现很大程度上取决于您正在使用的目标操作系统 操作系统最终提供了多线程能力 比如Linux有POSIX标准实现 而windows32有另一种方式 但我想知道编程语言水平的主要不同 C似乎为同步提供了更多选择 例如互斥锁

随机推荐