webapp Blobstore 处理程序

webapp 包含可与 Blobstore API 配合使用的请求处理程序类。BlobstoreUploadHandler 提供的逻辑可以将通过 Blobstore 传递的上传请求解析为 BlobInfo 记录,以便进一步处理。BlobstoreDownloadHandler 可帮助您轻松传送任何路径的 Blobstore 值。

BlobstoreUploadHandler

通过应用的用户或管理员发布的文件上传,将值添加到 Blobstore。应用会发布一个网络表单,其中包含文件上传字段以及将上传内容定向到 Blobstore 的表单操作。应用通过调用函数 create_upload_url(),向其传递在用户上传文件时要调用的应用处理程序的网址,以获取表单操作网址。webapp 应用程序可以使用 BlobstoreUploadHandler 类的子类作为此网址的处理程序。

get_uploads() 方法会返回一个 BlobInfo 对象列表,每个对象对应一个请求中上传的文件。每个对象均包含上传值的 Blobstore 键,以及文件名和大小等元数据。每个上传的文件在数据存储区中也有一个包含此信息的对应实体,因此您稍后可以使用给定的 blob 键来提取 BlobInfo 对象,或者对元数据字段执行数据存储区查询。上传处理程序直接从请求数据(而不是数据存储区)中解析此信息。

默认情况下,get_uploads() 将为请求中的所有上传文件返回 BlobInfo 对象。该方法还接受 field_name 参数,以便只获取给定文件上传字段的文件。返回值始终是一个列表,可以是空列表。

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())

BlobstoreUploadHandler 与 Google Cloud Storage 搭配使用

如果您将此上传处理程序与 Cloud Storage 结合使用,则需要获取并存储完整的 Cloud Storage 对象文件名,因为再次从 Cloud Storage 检索该文件时需要该文件名。 使用函数 get_file_infos,该函数会返回与每次上传对应的 FileInfo 记录列表。FileInfo 中提供了完整的 Cloud Storage 对象名称、内容类型、创建时间和其他数据。(详情请参阅链接)

BlobstoreDownloadHandler

为了提供 Blobstore 值,该应用程序会将 X-AppEngine-BlobKey 标头设置为 Blobstore 键的值(字符串形式)。如果 App Engine 在响应中看到此标头,则会将 blob 的值作为响应的正文。使用 webapp 处理程序类 BlobstoreDownloadHandler 可轻松在响应中设置此值。

send_blob() 方法可接受 BlobKey 对象、字符串键或 BlobInfo 作为 blob_key_or_info 参数,并设置响应数据以便将 Blob 值投放给该用户。该方法接受一个可选的 content_type 参数,该参数将覆盖已存储的 blob 值的 MIME 内容类型。默认情况下,blob 与上传它的客户端所设置的内容类型、从文件名中派生的内容类型或者通用类型(如果没有其他类型的信息)信息一起传送。

send_blob() 方法接受一个 save_as 参数,该参数确定是将 blob 数据作为原始响应数据还是包含文件名的 MIME 附件发送。如果该参数是字符串,则将 blob 作为附件发送,并将字符串值用作文件名。如果 Trueblob_key_or_infoBlobInfo 对象,则使用该对象中的文件名。默认情况下,blob 数据将作为响应正文发送,而不是作为 MIME 附件发送。

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)

除了发送完整的值,Blobstore 也支持只发送值的一部分,具体由字节索引范围指定。您可以使用两种方式为 BlobstoreDownloadHandlersend_blob() 方法提供字节索引范围。第一种方法:将该范围指定为参数 startend

            # Send the first 1,000 bytes of the value.
            self.send_blob(key, start=0, end=999)

默认情况下,BlobstoreDownloadHandler 将采用请求中的 range 标头。如果需要阻止使用原始范围标头,请将 use_range=False 参数提供给 send_blob()

            # Send the full value of the blob and
            # block the "range" header.
            self.send_blob(key, use_range=False)

range 标头的值是一个标准 HTTP 字节范围BlobstoreDownloadHandler 使用 webob.byterange 解析此标头值。

完整示例应用

在以下示例应用中,应用的主网址会加载要求用户上传文件的表单,而且上传处理程序会立即调用下载处理程序来传送数据。这是为了简化示例应用。在实际运用中,您可能不会使用主网址来请求上传数据,也不会立即传送您刚上传的 blob。

from google.appengine.api import users
from google.appengine.ext import blobstore
from google.appengine.ext import ndb
from google.appengine.ext.webapp import blobstore_handlers
import webapp2


# This datastore model keeps track of which users uploaded which photos.
class UserPhoto(ndb.Model):
    user = ndb.StringProperty()
    blob_key = ndb.BlobKeyProperty()


class PhotoUploadFormHandler(webapp2.RequestHandler):
    def get(self):
        upload_url = blobstore.create_upload_url('/upload_photo')
        # To upload files to the blobstore, the request method must be "POST"
        # and enctype must be set to "multipart/form-data".
        self.response.out.write("""
<html><body>
<form action="{0}" method="POST" enctype="multipart/form-data">
  Upload File: <input type="file" name="file"><br>
  <input type="submit" name="submit" value="Submit">
</form>
</body></html>""".format(upload_url))


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)