适用于 Python 3 的 Blobstore API

本页面介绍如何将 Blobstore API(一种旧版捆绑服务)与标准环境中的 Python 3 运行时搭配使用。您的应用可以通过 Python 3 版 App Engine 服务 SDK 访问捆绑服务。

概览

由于 Python 3 不支持 webapp,因此在将 Blobstore 处理程序代码从 Python 2 迁移到 Python 3 时,需要进行一些细微更改。要使用适用于 Python 3 的 Blobstore API,请注意以下事项:

  • Blobstore 处理程序类是实用程序类。这意味着处理程序类不再基于 webapp,因此您无法使用 webapp 软件包 (google.appengine.ext.webapp) 提供的 blobstore_handlers 模块或这些处理程序的子类中的 webapp2.RequestHandler 参数。

  • Blobstore 处理程序类中的所有方法都需要将 WSGI environ 字典作为输入参数。

以下部分介绍了如何在 Flask 应用中使用适用于 Python 3 的 BlobstoreUploadHandlerBlobstoreDownloadHandler 类,以及不使用 Python 框架的 WSGI 应用。您可以将 Python 3 示例与 Python 2 示例代码进行比较,以详细了解代码更改差异。

示例:Flask 应用

在 Python 3 中,Blobstore 处理程序类是模块 google.appengine.ext.blobstore 的一部分。对于 Flask 应用,对 BlobstoreUploadHandlerBlobstoreDownloadHandler 类中的方法进行的所有调用都需要 request.environ 字典(需要从 flask 模块导入 request)。

比较从 Python 2 (webapp2) 到 Python 3 (Flask) 发生的代码更改。请注意 Flask 应用在 get_uploads()send_blob() 方法中使用 request.environ 参数的方式:

Python 2 (webapp2)

class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(),
            blob_key=upload.key())
        user_photo.put()

        self.redirect('/view_photo/%s' % upload.key())

class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)

app = webapp2.WSGIApplication([
    ('/', PhotoUploadFormHandler),
    ('/upload_photo', PhotoUploadHandler),
    ('/view_photo/([^/]+)?', ViewPhotoHandler),
], debug=True)

Python 3 (Flask)

class PhotoUploadHandler(blobstore.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads(request.environ)[0]
        photo = PhotoUpload(blob_key=upload.key())
        photo.put()

        return redirect("/view_photo/%s" % upload.key())

class ViewPhotoHandler(blobstore.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            return "Photo key not found", 404
        else:
            headers = self.send_blob(request.environ, photo_key)

            # Prevent Flask from setting a default content-type.
            # GAE sets it to a guessed type if the header is not set.
            headers["Content-Type"] = None
            return "", headers

@app.route("/view_photo/<photo_key>")
def view_photo(photo_key):
    """View photo given a key."""
    return ViewPhotoHandler().get(photo_key)

@app.route("/upload_photo", methods=["POST"])
def upload_photo():
    """Upload handler called by blobstore when a blob is uploaded in the test."""
    return PhotoUploadHandler().post()

如需查看 Python 3 (Flask) 的完整代码示例,请参阅 GitHub

示例:没有网络框架的 WSGI 应用

以下 Python 3(WSGI 应用)代码展示了如何在没有网络框架的 WSGI 应用上使用 Blobstore 处理程序类时添加 environ 参数。注意 get_uploads()send_blob() 方法如何使用 environ 参数,并将其与 Python 2 版本进行比较:

Python 2

class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(),
            blob_key=upload.key())
        user_photo.put()

        self.redirect('/view_photo/%s' % upload.key())

class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)

app = webapp2.WSGIApplication([
    ('/', PhotoUploadFormHandler),
    ('/upload_photo', PhotoUploadHandler),
    ('/view_photo/([^/]+)?', ViewPhotoHandler),
], debug=True)

Python 3

class UploadPhotoHandler(blobstore.BlobstoreUploadHandler):
    """Upload handler called by blobstore when a blob is uploaded in the test."""

    def post(self, environ):
        upload = self.get_uploads(environ)[0]
        user_photo = UserPhoto(blob_key=upload.key())
        user_photo.put()

        # Redirect to the '/view_photo/<Photo Key>' URL
        return (
            "",
            http.HTTPStatus.FOUND,
            [("Location", "/view_photo/%s" % upload.key())],
        )

class ViewPhotoHandler(blobstore.BlobstoreDownloadHandler):
    def get_photo(self, environ, photo_key):
        if not blobstore.get(photo_key):
            return "Photo key not found", http.HTTPStatus.NOT_FOUND, []
        else:
            return (
                "",
                http.HTTPStatus.OK,
                list(self.send_blob(environ, photo_key).items()),
            )

    def get(self, environ):
        photo_key = (environ["app.url_args"])[0]
        return self.get_photo(environ, photo_key)

# map urls to functions
urls = [
    (r"^$", UploadFormHandler),
    (r"upload_photo/?$", UploadPhotoHandler),
    (r"view_photo/(.+)$", ViewPhotoHandler),
]

如需查看 Python 3 的完整代码示例,请参阅 GitHub