本指南將說明如何從 App Engine Blobstore 遷移至 Cloud Storage。
Cloud Storage 與 App Engine Blobstore 相似,您可以使用 Cloud Storage 提供大型資料物件 (blob),例如影片或圖片檔,並讓使用者上傳大型資料檔案。雖然 App Engine Blobstore 只能透過 App Engine 舊版套裝服務存取,但 Cloud Storage 是可透過 Cloud 用戶端程式庫存取的獨立 Google Cloud產品。Cloud Storage 可為應用程式提供更先進的物件儲存空間解決方案,讓您日後能靈活地遷移至 Cloud Run 或其他應用程式代管平台。 Google Cloud
對於 Google Cloud 2016 年 11 月後建立的專案,Blobstore 會在幕後使用 Cloud Storage 值區。也就是說,當您將應用程式遷移至 Cloud Storage 時,這些現有 Cloud Storage 值區中的所有現有物件和權限都不會變更。您也可以使用 Cloud Storage 適用的 Cloud 用戶端程式庫,開始存取這些現有的值區。
主要差異和相似之處
Cloud Storage 會排除下列 Blobstore 依附元件和限制:
- Python 2 適用的 Blobstore API 會依賴 webapp。
- Python 3 適用的 Blobstore API 會使用公用程式類別來使用 Blobstore 處理程序。
- 對於 Blobstore,可上傳至 Blobstore 的檔案數量上限為 500 個。您可以在 Cloud Storage 值區中建立的物件數量沒有限制。
Cloud Storage 不支援以下項目:
- Blobstore 處理常式類別
- Blobstore 物件
Cloud Storage 和 App Engine Blobstore 的相似之處:
- 能夠在執行階段環境中讀取及寫入大型資料物件,以及儲存及提供靜態大型資料物件,例如電影、圖片或其他靜態內容。Cloud Storage 的物件大小限制為 5 TiB。
- 可將物件儲存在 Cloud Storage 值區中。
- 提供免費方案。
事前準備
- 請詳閱並瞭解 Cloud Storage 的定價和配額:
- Cloud Storage 是付費使用服務,其資料儲存空間的定價會根據資料的儲存空間級別和值區位置而定。
- Cloud Storage 配額與 App Engine Blobstore 配額和限制有些不同,可能會影響您的 App Engine 要求配額。
- 您必須有使用 Blobstore 的現有 Python 2 或 Python 3 App Engine 應用程式。
- 本指南的範例說明瞭使用 Flask 架構遷移至 Cloud Storage 的應用程式。請注意,您可以使用任何網路架構,包括在遷移至 Cloud Storage 時繼續使用
webapp2
。
總覽
從 App Engine Blobstore 遷移至 Cloud Storage 的程序大致包含下列步驟:
- 更新設定檔
- 更新 Python 應用程式:
- 更新網路架構
- 匯入及初始化 Cloud Storage
- 更新 Blobstore 處理常式
- 選用:如果使用 Cloud NDB 或 App Engine NDB,請更新資料模型
- 測試及部署應用程式
更新設定政策
修改應用程式程式碼,將 Blobstore 遷移至 Cloud Storage 之前,請先更新設定檔,以便使用 Cloud Storage 程式庫。
更新
app.yaml
檔案。請按照您使用的 Python 版本的操作說明操作:Python 2
Python 2 應用程式:
- 移除
handlers
區段,以及libraries
區段中的任何不必要的 webapp-dependencies。 - 如果您使用 Cloud 用戶端程式庫,請新增最新版本的
grpcio
和setuptools
程式庫。 - 新增
ssl
程式庫,因為 Cloud Storage 需要這個程式庫。
以下是經過變更的
app.yaml
檔案範例:runtime: python27 threadsafe: yes api_version: 1 handlers: - url: /.* script: main.app libraries: - name: grpcio version: latest - name: setuptools version: latest - name: ssl version: latest
Python 3
如果是 Python 3 應用程式,請刪除
runtime
元素以外的所有行。例如:runtime: python310 # or another support version
Python 3 執行階段會自動安裝程式庫,因此您不需要指定先前 Python 2 執行階段內建的程式庫。如果您的 Python 3 應用程式在遷移至 Cloud Storage 時使用其他舊版內含服務,請保留
app.yaml
檔案。- 移除
更新
requirements.txt
檔案。請按照您使用的 Python 版本操作說明進行操作:Python 2
將 Cloud Storage 適用的 Cloud 用戶端程式庫新增至
requirements.txt
檔案中的依附元件清單。google-cloud-storage
接著執行
pip install -t lib -r requirements.txt
,更新應用程式可用的程式庫清單。Python 3
將 Cloud Storage 的 Cloud 用戶端程式庫新增至
requirements.txt
檔案中的依附元件清單。google-cloud-storage
在 Python 3 執行階段部署應用程式時,App Engine 會自動安裝這些依附元件,因此請刪除
lib
資料夾 (如果有)。針對 Python 2 應用程式,如果應用程式使用內建或複製的程式庫,您必須在
appengine_config.py
檔案中指定這些路徑:import pkg_resources from google.appengine.ext import vendor # Set PATH to your libraries folder. PATH = 'lib' # Add libraries installed in the PATH folder. vendor.add(PATH) # Add libraries to pkg_resources working set to find the distribution. pkg_resources.working_set.add_entry(PATH)
更新 Python 應用程式
修改設定檔後,請更新 Python 應用程式。
更新 Python 2 網頁架構
如果是使用 webapp2
架構的 Python 2 應用程式,建議您遷移至非過時的 webapp2
架構。如要瞭解 Python 2 的支援結束日期,請參閱執行階段支援時程。
您可以遷移至其他網路架構,例如 Flask、Django 或 WSGI。由於 Cloud Storage 會排除對 webapp2
的依附元件,且 Blobstore 處理常式不受支援,因此您可以刪除或取代其他與 webapp 相關的程式庫。
如果您選擇繼續使用 webapp2
,請注意,本指南中的範例會搭配 Flask 使用 Cloud Storage。
如果您除了使用 Cloud Storage 外,還打算使用 Google Cloud 服務,或是想存取最新的執行階段版本,建議您考慮將應用程式升級至 Python 3 執行階段。詳情請參閱「從 Python 2 遷移至 Python 3 的總覽」。
匯入及初始化 Cloud Storage
更新匯入和初始化行來修改應用程式檔案:
移除 Blobstore 匯入陳述式,例如:
import webapp2 from google.appengine.ext import blobstore from google.appengine.ext.webapp import blobstore_handlers
新增 Cloud Storage 和 Google 驗證程式庫的匯入陳述式,如下所示:
import io from flask import (Flask, abort, redirect, render_template, request, send_file, url_for) from google.cloud import storage import google.auth
Google 驗證程式庫需要取得與 Cloud Storage 的 Blobstore 相同的專案 ID。匯入其他程式庫 (例如 Cloud NBD),如果適用於您的應用程式。
為 Cloud Storage 建立新的用戶端,並指定 Blobstore 中使用的值區。例如:
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID
對於 Google Cloud 2016 年 11 月後的專案,Blobstore 會將資料寫入以應用程式網址命名的 Cloud Storage 值區,並遵循
PROJECT_ID.appspot.com
的格式。您可以使用 Google 驗證機制取得專案 ID,指定用於在 Blobstore 中儲存 blob 的 Cloud Storage 值區。
更新 Blobstore 處理常式
由於 Cloud Storage 不支援 Blobstore 上傳和下載處理常式,因此您必須結合使用 Cloud Storage 功能、io
標準程式庫模組、您的網路架構和 Python 公用程式,才能在 Cloud Storage 中上傳及下載物件 (Blob)。
以下示範如何使用 Flask 做為範例網頁架構,更新 Blobstore 處理常式:
請在 Flask 中,將 Blobstore 上傳處理常式類別替換為上傳函式。請按照您使用的 Python 版本操作說明進行操作:
Python 2
Python 2 中的 Blobstore 處理程序是
webapp2
類別,如以下 Blobstore 範例所示:class UploadHandler(blobstore_handlers.BlobstoreUploadHandler): 'Upload blob (POST) handler' def post(self): uploads = self.get_uploads() blob_id = uploads[0].key() if uploads else None store_visit(self.request.remote_addr, self.request.user_agent, blob_id) self.redirect('/', code=307) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
如要使用 Cloud Storage,請按照下列步驟操作:
- 將 Webapp 上傳類別替換為 Flask 上傳函式。
- 將上傳處理常式和路由替換為使用路由裝飾的 Flask
POST
方法。
更新後的程式碼範例:
@app.route('/upload', methods=['POST']) def upload(): 'Upload blob (POST) handler' fname = None upload = request.files.get('file', None) if upload: fname = secure_filename(upload.filename) blob = gcs_client.bucket(BUCKET).blob(fname) blob.upload_from_file(upload, content_type=upload.content_type) store_visit(request.remote_addr, request.user_agent, fname) return redirect(url_for('root'), code=307)
在更新後的 Cloud Storage 程式碼範例中,應用程式現在會根據物件名稱 (
fname
) 而非blob_id
來識別物件構件。路由也會在應用程式檔案的底部進行。如要取得已上傳的物件,請將 Blobstore 的
get_uploads()
方法替換為 Flask 的request.files.get()
方法。在 Flask 中,您可以使用secure_filename()
方法取得檔案的名稱,而無需使用路徑字元 (例如/
),然後使用gcs_client.bucket(BUCKET).blob(fname)
指定值區名稱和物件名稱,藉此識別物件。Cloud Storage
upload_from_file()
呼叫會執行上傳作業,如更新後的範例所示。Python 3
Blobstore for Python 3 中的上傳處理常式類別是公用程式類別,需要使用 WSGI
environ
字典做為輸入參數,如以下 Blobstore 範例所示:class UploadHandler(blobstore.BlobstoreUploadHandler): 'Upload blob (POST) handler' def post(self): uploads = self.get_uploads(request.environ) if uploads: blob_id = uploads[0].key() store_visit(request.remote_addr, request.user_agent, blob_id) return redirect('/', code=307) ... @app.route('/upload', methods=['POST']) def upload(): """Upload handler called by blobstore when a blob is uploaded in the test.""" return UploadHandler().post()
如要使用 Cloud Storage,請將 Blobstore 的
get_uploads(request.environ)
方法替換為 Flask 的request.files.get()
方法。更新後的程式碼範例:
@app.route('/upload', methods=['POST']) def upload(): 'Upload blob (POST) handler' fname = None upload = request.files.get('file', None) if upload: fname = secure_filename(upload.filename) blob = gcs_client.bucket(BUCKET).blob(fname) blob.upload_from_file(upload, content_type=upload.content_type) store_visit(request.remote_addr, request.user_agent, fname) return redirect(url_for('root'), code=307)
在更新後的 Cloud Storage 程式碼範例中,應用程式現在會根據物件名稱 (
fname
) 而非blob_id
來識別物件構件。路由也會在應用程式檔案的底部進行。如要取得已上傳的物件,請將 Blobstore 的
get_uploads()
方法替換為 Flask 的request.files.get()
方法。在 Flask 中,您可以使用secure_filename()
方法取得檔案的名稱,而無需使用路徑字元 (例如/
),然後使用gcs_client.bucket(BUCKET).blob(fname)
指定值區名稱和物件名稱,藉此識別物件。Cloud Storage
upload_from_file()
方法會執行上傳作業,如更新後的範例所示。請在 Flask 中將 Blobstore 下載處理常式類別替換為下載函式。請按照您使用的 Python 版本操作說明進行操作:
Python 2
以下下載處理常例說明瞭
BlobstoreDownloadHandler
類別的用法,該類別會使用 webapp2:class ViewBlobHandler(blobstore_handlers.BlobstoreDownloadHandler): 'view uploaded blob (GET) handler' def get(self, blob_key): self.send_blob(blob_key) if blobstore.get(blob_key) else self.error(404) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
如要使用 Cloud Storage,請按照下列步驟操作:
- 更新 Blobstore 的
send_blob()
方法,以使用 Cloud Storage 的download_as_bytes()
方法。 - 將路由從 webapp2 變更為 Flask。
更新後的程式碼範例:
@app.route('/view/<path:fname>') def view(fname): 'view uploaded blob (GET) handler' blob = gcs_client.bucket(BUCKET).blob(fname) try: media = blob.download_as_bytes() except exceptions.NotFound: abort(404) return send_file(io.BytesIO(media), mimetype=blob.content_type)
在更新後的 Cloud Storage 程式碼範例中,Flask 會修飾 Flask 函式中的路徑,並使用
'/view/<path:fname>'
識別物件。Cloud Storage 會根據物件名稱和值區名稱識別blob
物件,並使用download_as_bytes()
方法以位元組下載物件,而非使用 Blobstore 的send_blob
方法。如果找不到構件,應用程式會傳回 HTTP404
錯誤。Python 3
與上傳處理常式一樣,Blobstore for Python 3 中的下載處理常式類別也是公用類別,且必須使用 WSGI
environ
字典做為輸入參數,如以下 Blobstore 範例所示:class ViewBlobHandler(blobstore.BlobstoreDownloadHandler): 'view uploaded blob (GET) handler' def get(self, blob_key): if not blobstore.get(blob_key): return "Photo key not found", 404 else: headers = self.send_blob(request.environ, blob_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/<blob_key>') def view_photo(blob_key): """View photo given a key.""" return ViewBlobHandler().get(blob_key)
如要使用 Cloud Storage,請將 Blobstore 的
send_blob(request.environ, blob_key)
替換為 Cloud Storage 的blob.download_as_bytes()
方法。更新後的程式碼範例:
@app.route('/view/<path:fname>') def view(fname): 'view uploaded blob (GET) handler' blob = gcs_client.bucket(BUCKET).blob(fname) try: media = blob.download_as_bytes() except exceptions.NotFound: abort(404) return send_file(io.BytesIO(media), mimetype=blob.content_type)
在更新後的 Cloud Storage 程式碼範例中,
blob_key
已替換為fname
,Flask 會使用'/view/<path:fname>'
網址識別物件。gcs_client.bucket(BUCKET).blob(fname)
方法用於找出檔案名稱和 bucket 名稱。Cloud Storage 的download_as_bytes()
方法會以位元組下載物件,而非使用 Blobstore 的send_blob()
方法。- 更新 Blobstore 的
如果應用程式使用主處理常式,請在 Flask 中將
MainHandler
類別替換為root()
函式。請按照您使用的 Python 版本的操作說明操作:Python 2
以下是使用 Blobstore 的
MainHandler
類別的範例:class MainHandler(BaseHandler): 'main application (GET/POST) handler' def get(self): self.render_response('index.html', upload_url=blobstore.create_upload_url('/upload')) def post(self): visits = fetch_visits(10) self.render_response('index.html', visits=visits) app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
如要使用 Cloud Storage,請按照下列步驟操作:
- 移除
MainHandler(BaseHandler)
類別,因為 Flask 會為您處理路由。 - 使用 Flask 簡化 Blobstore 程式碼。
- 移除結尾的 webapp 路由。
更新後的程式碼範例:
@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
Python 3
如果您使用 Flask,就不會有
MainHandler
類別,但如果使用 blobstore,則需要更新 Flask 根函式。以下範例使用blobstore.create_upload_url('/upload')
函式:@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = blobstore.create_upload_url('/upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
如要使用 Cloud Storage,請將
blobstore.create_upload_url('/upload')
函式替換為 Flask 的url_for()
方法,以便取得upload()
函式的網址。更新後的程式碼範例:
@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') # Updated to use url_for else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
- 移除
測試及部署應用程式
本機開發伺服器可讓您測試應用程式的執行情形,但必須部署新版本才能測試 Cloud Storage,因為所有 Cloud Storage 要求都必須透過網際網路傳送至實際的 Cloud Storage 值區。如要瞭解如何在本機執行應用程式,請參閱「測試及部署應用程式」。然後部署新版本,確認應用程式顯示內容與先前相同。
使用 App Engine NDB 或 Cloud NDB 的應用程式
如果您的應用程式使用 App Engine NDB 或 Cloud NDB 來納入 Blobstore 相關的屬性,則必須更新 Datastore 資料模型。
更新資料模型
由於 Cloud Storage 不支援 NDB 的 BlobKey
屬性,因此您需要修改 Blobstore 相關行,以便使用 NDB、網路架構或其他位置內建的等效屬性。
如要更新資料模型,請按照下列步驟操作:
找出資料模型中使用
BlobKey
的程式碼行,如下所示:class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.BlobKeyProperty()
將
ndb.BlobKeyProperty()
替換為ndb.StringProperty()
:class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.StringProperty() # Modified from ndb.BlobKeyProperty()
如果您在遷移期間也要從 App Engine NDB 升級至 Cloud NDB,請參閱 Cloud NDB 遷移指南,瞭解如何重構 NDB 程式碼,以便使用 Python 內容管理工具。
Datastore 資料模型的回溯相容性
在上一節中,將 ndb.BlobKeyProperty
替換為 ndb.StringProperty
會導致應用程式無法回溯相容,也就是說,應用程式將無法處理 Blobstore 建立的舊項目。如果您需要保留舊資料,請為新的 Cloud Storage 項目建立額外欄位,而非更新 ndb.BlobKeyProperty
欄位,並建立函式來將資料標準化。
根據前幾節的範例,請進行以下變更:
定義資料模型時,請建立兩個不同的資源欄位。使用
file_blob
屬性來識別 Blobstore 建立的物件,並使用file_gcs
屬性來識別 Cloud Storage 建立的物件:class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.BlobKeyProperty() # backwards-compatibility file_gcs = ndb.StringProperty()
找出參照新訪客的資料列,如下所示:
def store_visit(remote_addr, user_agent, upload_key): 'create new Visit entity in Datastore' with ds_client.context(): Visit(visitor='{}: {}'.format(remote_addr, user_agent), file_blob=upload_key).put()
變更程式碼,讓
file_gcs
用於近期項目。例如:def store_visit(remote_addr, user_agent, upload_key): 'create new Visit entity in Datastore' with ds_client.context(): Visit(visitor='{}: {}'.format(remote_addr, user_agent), file_gcs=upload_key).put() # change file_blob to file_gcs for new requests
建立新函式來規範資料。以下範例說明如何使用擷取、轉換和載入 (ETL) 來迴圈處理所有造訪,並使用訪客和時間戳記資料,檢查是否存在
file_gcs
或file_gcs
:def etl_visits(visits): return [{ 'visitor': v.visitor, 'timestamp': v.timestamp, 'file_blob': v.file_gcs if hasattr(v, 'file_gcs') \ and v.file_gcs else v.file_blob } for v in visits]
找出參照
fetch_visits()
函式的行:@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
在
etl_visits()
函式中包裝fetch_visits()
,例如:@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = etl_visits(fetch_visits(10)) # etl_visits wraps around fetch_visits return render_template('index.html', **context)
範例
- 如要查看如何將 Python 2 應用程式遷移至 Cloud Storage 的範例,請比較 GitHub 中的 Blobstore 適用於 Python 2 的程式碼範例和 Cloud Storage 程式碼範例。
- 如要查看如何將 Python 3 應用程式遷移至 Cloud Storage 的範例,請比較 GitHub 中的 Blobstore 適用於 Python 3 的程式碼範例和 Cloud Storage 程式碼範例。
後續步驟
- 如需實作教學課程,請參閱 從 App Engine Blobstore 遷移至 Cloud Storage 的 Python 程式碼研究室。
- 瞭解如何透過 Cloud Storage 儲存及提供靜態檔案。
- 詳情請參閱 Cloud Storage 說明文件。