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 值。網頁應用程式處理常式類別 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 標頭。如要封鎖對原始範圍標頭的使用,請為 send_blob() 提供引數 use_range=False

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