本指南說明如何從 App Engine Blobstore 遷移至 Cloud Storage。
Cloud Storage 與 App Engine Blobstore 類似,您可以使用 Cloud Storage 提供大型資料物件 (blob),例如影片或圖片檔,並讓使用者上傳大型資料檔案。App Engine Blobstore 只能透過 App Engine 舊版套裝服務存取,但 Cloud Storage 是獨立的 Google Cloud產品,可透過 Cloud 用戶端程式庫存取。Cloud Storage 為應用程式提供更現代化的物件儲存解決方案,並讓您日後能彈性遷移至 Cloud Run 或其他 Google Cloud 應用程式 Google Cloud 代管平台。
如果是 2016 年 11 月後建立的 Google Cloud 專案,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 bucket 中。
- 提供免費方案。
事前準備
- 請詳閱並瞭解 Cloud Storage 定價和配額:
- Cloud Storage 是付費服務,資料儲存的定價取決於資料的儲存空間級別和值區位置。
- Cloud Storage 配額與 App Engine Blobstore 配額和限制有些不同,可能會影響 App Engine 要求配額。
- 現有的 Python 2 或 Python 3 App Engine 應用程式使用 Blobstore。
- 本指南中的範例會顯示使用 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區段中任何不必要的網頁應用程式依附元件。 - 如果您使用 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: latestPython 3
如果是 Python 3 應用程式,請刪除
runtime元素以外的所有行。 例如:runtime: python310 # or another support versionPython 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 網頁架構
如果 Python 2 應用程式使用 webapp2 框架,建議您從過時的 webapp2 框架遷移。如要瞭解 Python 2 的終止支援日期,請參閱「執行階段支援時間表」。
您可以遷移至其他網路架構,例如 Flask、Django 或 WSGI。由於 Cloud Storage 排除對 webapp2 的依附元件,且不支援 Blobstore 處理常式,因此您可以刪除或取代其他與 webapp 相關的程式庫。
如果您選擇繼續使用 webapp2,請注意本指南中的範例會搭配 Flask 使用 Cloud Storage。
如果您打算使用 Google Cloud 服務 (除了 Cloud Storage 之外),或是想存取最新版本的執行階段,建議將應用程式升級至 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 驗證程式庫,才能取得 Blobstore 用於 Cloud Storage 的專案 ID。匯入其他程式庫,例如 Cloud NBD (如適用於您的應用程式)。
為 Cloud Storage 建立新用戶端,並指定 Blobstore 中使用的 bucket。例如:
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID如果是 2016 年 11 月之後的專案,Blobstore 會寫入以應用程式網址命名的 Cloud Storage 值區,並採用
PROJECT_ID.appspot.com格式。 Google Cloud 您可以使用 Google 驗證取得專案 ID,指定用於在 Blobstore 中儲存 Blob 的 Cloud Storage 值區。
更新 Blobstore 處理常式
由於 Cloud Storage 不支援 Blobstore 上傳和下載處理常式,您必須結合 Cloud Storage 功能、io標準程式庫模組、網路框架和 Python 公用程式,才能在 Cloud Storage 中上傳及下載物件 (Blob)。
以下範例說明如何使用 Flask 做為範例網頁框架,更新 Blobstore 處理常式:
將 Blobstore 上傳處理常式類別替換為 Flask 中的上傳函式。請按照您使用的 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
Python 3 的 Blobstore 上傳處理常式類別是公用程式類別,需要使用 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()方法會執行上傳作業,如更新後的範例所示。將 Blobstore 下載處理常式類別替換為 Flask 中的下載函式。請按照您使用的 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
與上傳處理常式類似,Python 3 的 Blobstore 下載處理常式類別也是公用程式類別,需要使用 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 程式碼。
- 最後移除網頁應用程式路徑。
更新的程式碼範例:
@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 bucket。如要瞭解如何在本機執行應用程式,請參閱「測試及部署應用程式」。然後部署新版本,確認應用程式顯示的內容與先前相同。
使用 App Engine NDB 或 Cloud NDB 的應用程式
如果應用程式使用 App Engine NDB 或 Cloud NDB,您必須更新 Datastore 資料模型,才能納入 Blobstore 相關屬性。
更新資料模型
由於 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)將
fetch_visits()包裝在etl_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 中的 Python 2 適用的 Blobstore 程式碼範例和 Cloud Storage 程式碼範例。
- 如要查看如何將 Python 3 應用程式遷移至 Cloud Storage 的範例,請比較 GitHub 中的 Python 3 適用的 Blobstore 程式碼範例和 Cloud Storage 程式碼範例。
後續步驟
- 如需實作教學課程,請參閱 Python 適用的「從 App Engine Blobstore 遷移至 Cloud Storage」程式碼研究室。
- 瞭解如何從 Cloud Storage 儲存及提供靜態檔案。
- 詳情請參閱 Cloud Storage 說明文件。