Blobstore Python API 總覽

注意事項:建議您透過 Google Cloud Storage (而非 Blobstore) 儲存 blob 資料。

Blobstore API 可讓您的應用程式提供名為 blob 的資料物件,這類物件會比 Datastore 服務允許的物件大小大上許多。Blob 不僅可用於提供影片或圖片檔等大型檔案外,還可讓使用者上傳大型資料檔案。Blob 的建立方式是透過 HTTP 要求上傳檔案。一般而言,應用程式會向使用者提供具備檔案上傳欄位的表單,藉此執行此操作。提交表單後,Blobstore 會從檔案內容建立 blob 並傳回 blob 的不透明參照,即「blob 鍵」,供您稍後用於提供該 blob。應用程式可根據使用者要求提供完整的 blob 值,或透過類似串流檔案的介面來直接讀取該值。

Blobstore 簡介

Google App Engine 包含的 Blobstore 服務可讓應用程式提供資料物件,並且只會受到單一 HTTP 連線可上傳或下載資料量的限制。這些物件稱為「Blobstore 值」或「blob」。 Blobstore 值會以要求處理常式所傳回的回應提供,並且透過網路表單的上傳作業建立而成。應用程式不會直接建立 blob 資料;相反地,blob 是透過提交網路表單或其他 HTTP POST 要求的方式間接建立。Blobstore 值可提供給使用者,或由應用程式透過 Blobstore API 在類似檔案的串流中存取。

應用程式會提供具備檔案上傳欄位的網路表單,藉此提示使用者上傳 Blobstore 值。應用程式會呼叫 Blobstore API 來產生表單的動作網址。使用者的瀏覽器會透過產生的網址,將檔案直接上傳至 Blobstore。接著 Blobstore 會儲存 blob 並修改要求,讓要求內容包含 blob 金鑰,並且將它傳送至應用程式中的特定路徑。在應用程式中,位於該路徑的要求處理常式可執行額外的表單處理。

為了提供 blob,應用程式會在外送回應中設定標頭,並且由 App Engine 將該回應取代為 blob 值。

Blob 一旦建立後便無法修改,但可予以刪除。每個 blob 在資料儲存庫中都有儲存對應的「blob 資訊記錄」,用以提供有關該 blob 的建立時間與內容類型等詳細資訊。您可以使用 blob 鍵來擷取 blob 資訊記錄並查詢其屬性。

應用程式可透過 API 呼叫,一次讀取 Blobstore 值的其中一個部分。讀取的部分最大可達 API 傳回值的大小上限。這個大小略低於 32 MB,在 Python 中是以常數 google.appengine.ext.blobstore.MAX_BLOB_FETCH_SIZE 表示。應用程式僅能透過使用者上傳的檔案來建立或修改 Blobstore 值。

使用 Blobstore

應用程式可使用 Blobstore 來接受使用者上傳的大型檔案,同時還可以提供這些檔案。檔案上傳之後,就稱為 blob。應用程式不會直接存取 blob,而是透過資料儲存庫中的「blob 資訊實體」(以 BlobInfo 類別表示) 使用 blob。

使用者可提交包含一或多個檔案輸入欄位的 HTML 表單,透過此方式建立 blob。應用程式會呼叫 create_upload_url() 來取得此表單的目的地 (動作),為這個函式傳送處理常式在應用程式的網址路徑。當使用者提交表單時,使用者的瀏覽器會將指定檔案直接上傳至 Blobstore。Blobstore 會重寫使用者的要求並儲存上傳的檔案資料,將上傳的檔案資料取代為一或多個對應的 blob 鍵,接著將重寫的要求傳送至您提供給 create_upload_url() 的網址路徑所在的處理常式。這個處理常式可根據 blob 鍵執行額外的處理工作。

應用程式可以透過類似檔案的串流介面讀取一部分的 Blobstore 值,相關資訊請見 BlobReader 類別說明。

上傳 blob

如要建立並上傳 blob,請依照下列程序執行:

1. 建立上傳網址

呼叫 blobstore.create_upload_url() 建立要讓使用者填寫的表單上傳網址,在表單的 POST 完成時傳送要載入的應用程式路徑。

upload_url = blobstore.create_upload_url('/upload_photo')

非同步版本為 create_upload_url_async()。此版本可讓應用程式的程式碼在 Blobstore 產生上傳網址時繼續執行。

2. 建立上傳表單

表單必須包含檔案上傳欄位,且表單的 enctype 必須設為 multipart/form-data。使用者提交表單時,POST 將由建立 blob 的 Blobstore API 來處理。API 另外也會建立 blob 的資訊記錄並儲存在資料儲存庫中,然後透過指定路徑將重寫的要求做為 blob 鍵傳送至應用程式。

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

3. 實作上傳處理常式

在這個處理常式中,您可以將 blob 鍵與應用程式資料模型的其他部分儲存在一起。blob 鍵本身仍可從資料儲存庫中的 blob 資訊實體存取。請注意,在使用者提交表單並呼叫處理常式後,blob 就已經儲存,而 blob 資訊也已新增至資料儲存庫。如果您不想在應用程式保留 blob,您應立即刪除 blob,以防止 blob 成為孤立檔案:

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

webapp 架構提供 blobstore_handlers.BlobstoreUploadHandler 上傳處理常式類別來協助您剖析表單資料。詳情請參閱 BlobstoreUploadHandler 的參考資料。

當 Blobstore 重寫使用者的要求時,已上傳檔案的 MIME 部分會將本文清空,並將 blob 鍵新增為 MIME 部分的標頭。系統會保留其他表單欄位與區段,並傳送至上傳處理常式。如果您未指定內容類型,Blobstore 會嘗試從副檔名推論。如果系統無法判定內容類型,則會為新建的 blob 指派內容類型 application/octet-stream

提供 blob

如要提供 blob,您必須以應用程式路徑的形式加入 blob 下載處理常式。應用程式會在連出回應中設定標頭,藉此提供 blob。下文中的範例使用的是 webapp 架構。您在使用 webapp 時,處理常式應該會將所需 blob 的 blob 鍵傳送至 self.send_blob()。在這個範例中,blob 鍵是做為網址的一部分傳送至下載處理常式。在實際操作時,下載處理常式可透過您選擇的任何方法 (例如透過其他方法或使用者動作) 來取得 blob 鍵。

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)

webapp 架構提供下載處理常式類別 blobstore_handlers.BlobstoreDownloadHandler 來協助您剖析表單資料。詳情請參閱 BlobstoreDownloadHandler 的參考資料。

Blob 可透過任何應用程式網址提供。為了在應用程式中提供 blob,您必須在包含 blob 鍵的回應中放上特殊標頭。App Engine 會以 blob 的內容取代回應的本文。

Blob 位元組範圍

Blobstore 可根據要求,提供某個極大值的一部分,而非完整的值。如要提供部分值,請在外送回應中加入 X-AppEngine-BlobRange 標頭。此標頭的值代表標準的 HTTP 位元組範圍。位元組編號從零開始。空白的 X-AppEngine-BlobRange 會指示 API 忽略範圍標頭,並提供完整的 blob。範例範圍包括:

  • 0-499 提供值的前 500 個位元組 (從第 0 個位元組至第 499 個位元組,包含首尾)。
  • 500-999 提供從第 501 個位元組開始的 500 個位元組。
  • 500- 提供從值的第 501 個位元組開始到值結尾的所有位元組。
  • -500 提供值的最後 500 個位元組。

如果位元組範圍對 Blobstore 值有效,Blobstore 會將 206 Partial Content 狀態碼和要求的位元組範圍傳送至用戶端。如果範圍對值無效,Blobstore 會傳送 416 Requested Range Not Satisfiable

Blobstore 不支援在單一要求中提供多個位元組範圍 (例如 100-199,200-299),即便範圍重複也一樣。

webapp.blobstore_handlers.BlobstoreDownloadHandler 類別提供的功能包括透過指定的位元組索引設定這個標頭,以及運用使用者提供的 range 標頭自動取得位元組範圍。

完整的應用程式範例

在下列應用程式範例中,應用程式的主要網址會載入可用來向使用者要求上傳檔案的表單,接著上傳處理常式會立即呼叫下載處理常式來提供資料。此做法是為了簡化範例應用程式的內容。就實務而言,您可能不會使用主要網址來要求上傳資料,也不會立即提供剛剛才上傳的 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)

將圖片服務與 Blobstore 搭配使用

圖片服務可將 Blobstore 值做為轉換的來源。來源圖片的大小可達 Blobstore 值的上限大小。圖片服務仍會將轉換後的圖片傳回應用程式,因此轉換後的圖片必須小於 32 MB。這很適合用於建立由使用者上傳的大型相片縮圖。

如要瞭解如何將圖片服務與 Blobstore 值搭配使用,請參閱圖片服務說明文件

使用 Blobstore API 與 Google Cloud Storage

您可以使用 Blobstore API 將 blob 儲存至 Cloud Storage,而非儲存在 Blobstore。您必須按照 Google Cloud Storage 說明文件中所述方式設定值區,並在 blobstore.blobstore.create_upload_url gs_bucket_name 參數中指定值區和檔案名稱。在上傳處理常式中,您需要處理傳回的 FileInfo 中繼資料,並且明確儲存 Google Cloud Storage 檔案名稱以便稍後擷取 blob。

您也可以使用 Blobstore API 提供 Cloud Storage 物件。 下列程式碼片段示範如何執行此操作:

"""A sample app that operates on GCS files with blobstore API."""

import cloudstorage
from google.appengine.api import app_identity
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
import webapp2

# This handler creates a file in Cloud Storage using the cloudstorage
# client library and then reads the data back using the Blobstore API.
class CreateAndReadFileHandler(webapp2.RequestHandler):
    def get(self):
        # Get the default Cloud Storage Bucket name and create a file name for
        # the object in Cloud Storage.
        bucket = app_identity.get_default_gcs_bucket_name()

        # Cloud Storage file names are in the format /bucket/object.
        filename = '/{}/blobstore_demo'.format(bucket)

        # Create a file in Google Cloud Storage and write something to it.
        with cloudstorage.open(filename, 'w') as filehandle:
            filehandle.write('abcde\n')

        # In order to read the contents of the file using the Blobstore API,
        # you must create a blob_key from the Cloud Storage file name.
        # Blobstore expects the filename to be in the format of:
        # /gs/bucket/object
        blobstore_filename = '/gs{}'.format(filename)
        blob_key = blobstore.create_gs_key(blobstore_filename)

        # Read the file's contents using the Blobstore API.
        # The last two parameters specify the start and end index of bytes we
        # want to read.
        data = blobstore.fetch_data(blob_key, 0, 6)

        # Write the contents to the response.
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write(data)

        # Delete the file from Google Cloud Storage using the blob_key.
        blobstore.delete(blob_key)

# This handler creates a file in Cloud Storage using the cloudstorage
# client library and then serves the file back using the Blobstore API.
class CreateAndServeFileHandler(blobstore_handlers.BlobstoreDownloadHandler):

    def get(self):
        # Get the default Cloud Storage Bucket name and create a file name for
        # the object in Cloud Storage.
        bucket = app_identity.get_default_gcs_bucket_name()

        # Cloud Storage file names are in the format /bucket/object.
        filename = '/{}/blobstore_serving_demo'.format(bucket)

        # Create a file in Google Cloud Storage and write something to it.
        with cloudstorage.open(filename, 'w') as filehandle:
            filehandle.write('abcde\n')

        # In order to read the contents of the file using the Blobstore API,
        # you must create a blob_key from the Cloud Storage file name.
        # Blobstore expects the filename to be in the format of:
        # /gs/bucket/object
        blobstore_filename = '/gs{}'.format(filename)
        blob_key = blobstore.create_gs_key(blobstore_filename)

        # BlobstoreDownloadHandler serves the file from Google Cloud Storage to
        # your computer using blob_key.
        self.send_blob(blob_key)

app = webapp2.WSGIApplication([
    ('/', CreateAndReadFileHandler),
    ('/blobstore/read', CreateAndReadFileHandler),
    ('/blobstore/serve', CreateAndServeFileHandler)], debug=True)

使用 BlobReader

應用程式可以透過類似於 Python file 物件的介面讀取 Blobstore 值的資料。這個介面可讀取任何位元組位置開始的值,並可使用多重服務呼叫與緩衝作業,因此即使受到單次服務呼叫回應的大小限制,應用程式還是可以存取該值的完整大小。

BlobReader 類別可以將下列三個值當中的其中一個當做其建構函式的引數:

物件會採用常見的檔案方法來讀取該值。應用程式無法修改 Blobstore 值;寫入的檔案方法不會受到執行。

# Instantiate a BlobReader for a given Blobstore blob_key.
blob_reader = blobstore.BlobReader(blob_key)

# Instantiate a BlobReader for a given Blobstore blob_key, setting the
# buffer size to 1 MB.
blob_reader = blobstore.BlobReader(blob_key, buffer_size=1048576)

# Instantiate a BlobReader for a given Blobstore blob_key, setting the
# initial read position.
blob_reader = blobstore.BlobReader(blob_key, position=0)

# Read the entire value into memory. This may take a while depending
# on the size of the value and the size of the read buffer, and is not
# recommended for large values.
blob_reader_data = blob_reader.read()

# Write the contents to the response.
self.response.headers['Content-Type'] = 'text/plain'
self.response.write(blob_reader_data)

# Set the read position back to 0, then read and write 3 bytes.
blob_reader.seek(0)
blob_reader_data = blob_reader.read(3)
self.response.write(blob_reader_data)
self.response.write('\n')

# Set the read position back to 0, then read and write one line (up to
# and including a '\n' character) at a time.
blob_reader.seek(0)
for line in blob_reader:
    self.response.write(line)

提出非同步要求

應用程式可呼叫一些能在背景運作的 Blobstore 函式。Blobstore 執行要求時,應用程式能夠執行其他工作。若要提出要求,應用程式必須呼叫非同步函式。函式會立即傳回遠端程序呼叫 (RPC) 物件,這個物件代表了該項要求。應用程式必須取得該項要求的結果時,就會呼叫遠端程序呼叫 (RPC) 物件的 get_result() 方法。

如果服務在應用程式呼叫 get_result() 時尚未完成要求,該方法會等待要求完成 (或等到呼叫期限或發生錯誤)。該方法會傳回結果物件;如果執行要求時發生錯誤,則會發出例外狀況。以下列這個程式碼片段為例:

upload_url = blobstore.create_upload_url('/upload')
slow_operation()
self.response.out.write("""<form action="%s" method="POST"
                           enctype="multipart/form-data">""" % upload_url)

會變成:

upload_url_rpc = blobstore.create_upload_url_async('/upload')
slow_operation()
upload_url = upload_url_rpc.get_result()
self.response.out.write("""<form action="%s" method="POST"
                           enctype="multipart/form-data">""" % upload_url)

在這個範例中,應用程式會執行 slow_operation() 程式碼,在同一時間內,Blobstore 則是會產生上傳網址。

配額與限制

Blobstore 值所使用的空間會佔用「儲存資料 (可計費)」配額。資料儲存庫中的 blob 資訊實體會計入資料儲存庫相關的限額中。請注意,Google Cloud Storage 屬付費使用服務,您需按照 Cloud Storage 價目表支付費用。

如要進一步瞭解整個系統的安全配額,請參閱配額

除了整個系統的安全配額外,Blobstore 的使用配額還受到下列特別限制:

  • 應用程式透過單一 API 呼叫可讀取的 Blobstore 資料量上限為 32 MB。
  • 單一表單 POST 可上傳的檔案數量上限為 500 個。
本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Python 2 適用的 App Engine 標準環境