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 標準環境