使用 Django 访问 App Engine 标准环境 Python 3 的 bundled Blobstore 服务 API

发布时间:2026/10/2 2:15:49
使用 Django 访问 App Engine 标准环境 Python 3 的 bundled Blobstore 服务 API 示例工程【免费下载链接】python-docs-samplesCode samples used on cloud.google.com项目地址https://gitcode.com/GitHub_Trending/py/python-docs-samples点击查看免费下载在 App Engine 标准环境Python 3 运行时下应用可以通过appengine-python-standard提供的 bundled services 兼容层继续使用 Blobstore 服务。本指南以 python-docs-samples 仓库中 bundled-services/blobstore/django 目录的完整示例为蓝本讲解如何用 Django 框架实现文件上传与下载用户通过表单上传本地文件应用将文件存入 Blobstore 并重定向到展示页面由浏览器拉取并显示该文件。阅读本文后你将掌握 App Engine 标准环境 Python 3 下 bundled Blobstore API 的接入方式、Django 视图与 handler 的编排方法以及基于gcloud的真实部署与端到端测试流程。示例背景bundled Blobstore 服务在 Python 3 中的定位Blobstore 是 App Engine 提供的托管大对象二进制文件存储服务典型用途是保存用户上传的图片、视频等体积较大的文件。在 Python 2 时代应用通过google.appengine.ext.blobstore直接调用迁移到 Python 3 标准环境后这些服务以 bundled services 形式通过兼容层继续提供。根据 blobstore 目录的主 README该目录包含三个功能完全一致的应用分别使用三种 Web 框架编写Flask基于 Flask 的经典实现Django本指南主题App Engine bundled services 的 WSGI 原生功能不依赖任何 Web 框架三个应用在 App Engine 中的行为完全一致访问首页/会展示一个包含文件选择框与提交按钮的表单提交表单后应用将文件保存到 Blobstore随后重定向浏览器到展示页面由该页面拉取并显示文件。部署前提app.yaml中必须显式开启 bundled API在 Python 3 标准环境中使用 bundled servicesapp.yaml是关键配置。Django 示例的 app.yaml 内容如下runtime: python313 app_engine_apis: true handlers: - url: .* script: auto需要特别强调的是app_engine_apis: true这一行——它是启用 bundled services API包括 Blobstore、NDB、Deferred 等的开关。若缺失该配置google.appengine.ext.blobstore等模块将无法正常工作。runtime: python313表明本示例面向 Python 3.13 运行时仓库中requirements.txt也与此对应见下文。handlers将全部 URL 路由到自动检测的 WSGI 应用即main.py中导出的app变量。部署命令即主 README 所述的标准方式gcloud app deploy源码解析Django 应用中 Blobstore 的完整接入链路Django 版示例的核心实现位于 main.py全文件约 110 行却完整展示了从 WSGI 包装、数据模型、上传/下载 handler 到 URL 路由与 Django 配置的全部要素。关键导入与 WSGI 包装from django.conf import settings from django.core.wsgi import get_wsgi_application from django.http import HttpResponse from django.shortcuts import redirect from django.urls import path from google.appengine.api import wrap_wsgi_app from google.appengine.ext import blobstore from google.appengine.ext import ndb from google.cloud import loggingwrap_wsgi_app来自google.appengine.api是appengine-python-standard兼容层提供的 WSGI 包装器。它把 Blobstore 等 bundled services 的请求上下文注入到 Django 的 WSGI 应用中。blobstore提供上传 URL 生成、上传/下载 handler 基类ndb用于在 Datastore 中持久化 blob key。应用还通过google.cloud.logging创建了一个自定义 logger日志会出现在 Cloud Logging 控制台的 Custom Logs 下logging_client logging.Client() logger logging_client.logger(django-app-logs)文件末尾将包装后的 WSGI 应用导出app wrap_wsgi_app(get_wsgi_application())NDB 数据模型记录谁上传了哪张照片class UserPhoto(ndb.Model): blob_key ndb.BlobKeyProperty()这是一个非常精简的 Datastore 模型用于跟踪哪些用户上传了哪些照片。虽然示例仅保存blob_key一个属性但ndb.BlobKeyProperty()是与 Blobstore 对象关联的标准方式——通过该 key 即可定位 Blobstore 中的文件。上传 HandlerBlobstoreUploadHandlerclass PhotoUploadHandler(blobstore.BlobstoreUploadHandler): def post(self, environ): upload self.get_uploads(environ)[0] photo_key upload.key() user_photo UserPhoto(blob_keyphoto_key) user_photo.put() logger.log_text(Photo key: %s % photo_key) return redirect(view_photo, keyphoto_key)流程拆解self.get_uploads(environ)[0]从当前请求的 multipart 表单数据中取出第一个上传文件upload.key()获得 Blobstore 分配给该文件的 blob key将 blob key 存入UserPhoto并put()到 Datastore通过 logger 记录 key 便于排查redirect(view_photo, keyphoto_key)按 Django 命名路由跳转到展示页面。注意Django 视图是基于 WSGI 的因此 handler 的post(self, environ)接收environ字典而非 Django 的request对象调用时由薄封装层传入request.environ。下载 HandlerBlobstoreDownloadHandlerclass ViewPhotoHandler(blobstore.BlobstoreDownloadHandler): def get(self, environ, photo_key): if not blobstore.get(photo_key): return HttpResponse(Photo key not found, status404) else: response HttpResponse(headersself.send_blob(environ, photo_key)) # Prevent Django from setting a default content-type. # GAE sets it to a guessed type if the header is not set. response[Content-Type] None return response要点先用blobstore.get(photo_key)校验 key 是否存在不存在时返回 404 与提示文案self.send_blob(environ, photo_key)返回包含文件内容的响应头字典用它构造HttpResponse即可把 Blobstore 中的文件直接流式返回给浏览器关键技巧将Content-Type显式置为None。Django 默认会注入一个 content-type而 App Engine 的 Blobstore 会根据文件内容自动猜测正确的类型只有把该头置空猜测逻辑才会生效否则会覆盖为 Django 的默认值。URL 路由与薄视图封装def upload_form(request): Create the HTML form to upload a file. upload_url blobstore.create_upload_url(/upload_photo) response f htmlbody form action{upload_url} methodPOST enctypemultipart/form-data Upload File: input typefile namefilebr input typesubmit namesubmit valueSubmit /form /body/html return HttpResponse(response) def view_photo(request, key): View photo given a key. return ViewPhotoHandler().get(request.environ, key) def upload_photo(request): Upload handler called by blobstore when a blob is uploaded in the test. return PhotoUploadHandler().post(request.environ) urlpatterns ( path(, upload_form, nameupload_form), path(view_photo/key, view_photo, nameview_photo), path(upload_photo, upload_photo, nameupload_photo), )三个路由对应三个职责路由视图作用/upload_form调用blobstore.create_upload_url(/upload_photo)生成专用的上传 URL渲染包含文件选择框的 HTML 表单upload_photoupload_photoBlobstore 上传完成后的回调端点转调PhotoUploadHandler.postview_photo/keyview_photo按 blob key 展示文件转调ViewPhotoHandler.get这里体现了一个重要机制表单的action不是固定路径而是create_upload_url()动态生成的专用上传 URL。Blobstore 在上传完成后会向该 URL 的success_path这里是/upload_photo发起回调因此表单必须使用enctypemultipart/form-data文件字段名为file与测试代码中的files{file: f}对应。Django 运行时配置无需完整项目脚手架示例没有创建完整的 Django 项目结构而是用settings.configure()在单文件内完成最小化配置settings.configure( DEBUGTrue, SECRET_KEYthisisthesecretkey, ROOT_URLCONF__name__, MIDDLEWARE_CLASSES( django.middleware.common.CommonMiddleware, django.middleware.csrf.CsrfViewMiddleware, django.middleware.clickjacking.XFrameOptionsMiddleware, ), ALLOWED_HOSTS[*], )ROOT_URLCONF__name__让 Django 把当前模块内的urlpatterns作为根路由表ALLOWED_HOSTS[*]允许任意主机名访问这是 App Engine 多版本域名环境下的常用做法示例包含 CSRF 中间件但上传表单未使用 CSRF token——这仅适用于该演示场景生产环境需按实际安全策略处理。依赖清单requirements.txt 的实际内容Django 示例的 requirements.txt 与 Python 3.12 运行时对应Django6.1.1; python_version 3.12 django-environ0.13.0 google-cloud-logging3.5.0 appengine-python-standard0.2.3其中appengine-python-standard0.2.3是 bundled services 兼容层的核心依赖提供google.appengine.api.wrap_wsgi_app、google.appengine.ext.blobstore与google.appengine.ext.ndb等模块google-cloud-logging则用于示例中的自定义日志上报。端到端测试无需路由流量的版本隔离策略主 README 明确指出bundled services 只能在正确配置的 App Engine 应用中运行因此测试这些应用必须真正部署到 App Engine无法在本地模拟。每个应用版本都配套一个测试程序如 Django 版的 main_test.py遵循三步流程启动应用的新版本但不向其路由网络流量即gcloud app deploy --no-promote并指定随机版本号通过该版本的专属 URL{version_id}-dot-{project_id}.appspot.com用 Web 请求与之交互测试完成后删除新版本——由于它从未被路由流量删除是安全的。由于每个测试使用独立版本并将测试消息与 Web 请求定向到该版本多个测试可以并行运行而互不干扰。测试实现中的两个细节值得关注gcloud_cli()封装了gcloud command --quiet --formatjson --project {project_id}并解析 JSON 输出test_upload_and_view的完整断言链路GET /检查响应包含form action确认表单正常渲染用正则action(.*?)从表单中提取动态上传 URL以main.py自身作为测试文件POST到该 URLfiles{file: f}断言响应中包含bfrom google.appengine.api——即上传后的展示页正确回传了源码文件内容。测试使用backoff指数退避重试机制应对gcloud部署与 App 初始化的短暂延迟wait_for_app最多等待 300 秒直至应用可访问。与 Flask / 原生 WSGI 版本的异同对比 Flask 版 main.py两者核心逻辑完全一致差异仅体现在框架适配层Flask 版通过app.wsgi_app wrap_wsgi_app(app.wsgi_app, use_deferredTrue)包装 WSGI 应用且显式启用了 Deferred 任务Flask 版 handler 直接以无参形式接收请求post(self)通过request.environ访问 WSGI 环境Django 版则显式接收environ参数Flask 版通过路由装饰器app.route注册端点Django 版使用urlpatterns两者都在返回响应时将Content-Type置为None以允许 App Engine 按内容猜测类型——这是跨越框架的通用最佳实践。小结从 django/README.md 出发结合主 README 与完整源码可以看到在 App Engine 标准环境 Python 3 中接入 bundled Blobstore API核心步骤可归纳为在 app.yaml 中声明app_engine_apis: true依赖 requirements.txt 中的appengine-python-standard用wrap_wsgi_app(get_wsgi_application())包装 Django WSGI 应用继承BlobstoreUploadHandler/BlobstoreDownloadHandler处理上传与下载用blobstore.create_upload_url()生成表单上传地址用ndb.BlobKeyProperty在 Datastore 中登记 blob key通过gcloud app deploy部署并用 main_test.py 的版本隔离策略完成端到端验证。这套模式同样适用于仓库中 Deferred、Mail 等其他 bundled services 示例见 bundled-services 目录是 Python 3 标准环境迁移期复用既有 App Engine 服务的可靠参考。赞分享示例工程【免费下载链接】python-docs-samplesCode samples used on cloud.google.com项目地址https://gitcode.com/GitHub_Trending/py/python-docs-samples点击查看免费下载相关推荐LeetCode「First Unique Number」题解从 O(N²) 暴力到 O(1) 的「队列 哈希表」三阶演进LeetCode「First Unique Number」题解从 O N² 暴力到 O 1 的「队列 哈希表」三阶演进 本文以仓库文档 articles/示例工程python-docs-samples 实战在 App Engine Python 3 中接入 Bundled Deferred 服务Flask / Django / WSGI 三框架实现python docs samples 实战在 App Engine Python 3 中接入 Bundled Deferred 服务Flask / Dja示例工程深入解析 .NET Runtime 的栈缓冲区溢出防护Guard StackGS机制在 RyuJIT 中的实现深入解析 .NET Runtime 的栈缓冲区溢出防护Guard StackGS机制在 RyuJIT 中的实现 导读 本文基于 docs/design/c示例工程上一篇如何用Python一键备份QQ空间所有历史说说这个开源工具让你轻松搞定下一篇GetQzonehistory三步轻松备份QQ空间历史说说的免费开源工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考