搭配使用 Cloud Storage 與 Go

這部分 Go Bookshelf 教學課程說明範例應用程式如何將圖片儲存在 Cloud Storage 中。如要進一步瞭解將 Cloud Storage 與 Go 搭配使用的一般情況,請參閱 Cloud Storage 用戶端程式庫說明文件。

本頁面是多頁教學課程的一部分。如要從頭開始並閱讀設定操作說明,請前往 Go Bookshelf 應用程式頁面。

建立 Cloud Storage 值區

以下是如何建立 Cloud Storage 值區的操作說明。「值區」是 Cloud Storage 中保存資料的基本容器。

  1. 在終端機視窗中,輸入以下指令:

    gsutil mb gs://[YOUR-BUCKET-NAME]

    其中:

    • [YOUR-BUCKET-NAME] 是 Cloud Storage 值區的名稱。
  2. 如要在 bookshelf 應用程式中查看上傳的圖片,請將值區的預設存取控制清單 (ACL) 設定為 public-read

    gsutil defacl set public-read gs://[YOUR-BUCKET-NAME]

    設定

    1. 前往包含程式碼範例的目錄:

      Linux/macOS

      cd $GOPATH/src/github.com/GoogleCloudPlatform/golang-samples/getting-started/bookshelf
      

      Windows

      cd %GOPATH%\src\github.com\GoogleCloudPlatform\golang-samples\getting-started\bookshelf
      

    2. 開啟 config.go 進行編輯。

    3. 將下面這兩行取消註解:

      // StorageBucketName = "<your-storage-bucket>"
      // StorageBucket, err = configureStorage(StorageBucketName)
      
    4. <your-storage-bucket> 替換為您的 Cloud Storage 值區名稱。

    5. 儲存並關閉 config.go

    在本機電腦執行應用程式

    1. 執行應用程式以啟動本機網路伺服器:

      cd app
      go run app.go auth.go template.go
      
    2. 在網路瀏覽器中輸入此位址:

      http://localhost:8080

    現在您可以瀏覽應用程式的網頁、新增書籍和封面圖片、編輯書籍以及刪除書籍。

    按下 Control+C 即可離開本機網路伺服器。

    將應用程式部署至 App Engine 彈性環境

    1. app 目錄中,輸入下列指令以部署範例:

      gcloud app deploy
      
    2. 在網路瀏覽器中,輸入下列網址。將 [YOUR_PROJECT_ID] 替換為您的專案 ID:

      https://[YOUR_PROJECT_ID].appspot.com
      

    如果您更新應用程式,可輸入與第一次部署應用程式時使用的相同指令部署更新版本。新部署會為您的應用程式建立新版本,並會將它升級為預設版本。 應用程式的較舊版本會保留下來,相關聯的 VM 執行個體也會保留下來。請注意,這些應用程式版本和 VM 執行個體全部都是計費資源。

    您可以刪除應用程式的非預設版本來降低費用。

    若要刪除應用程式版本:

    1. 前往 GCP 主控台的「App Engine Versions」(App Engine 版本) 頁面。

      前往版本頁面

    2. 找到您要刪除的非預設應用程式版本,然後點選旁邊的核取方塊。
    3. 按一下頁面頂部的 [刪除] 按鈕, 刪除應用程式版本。

    如需有關清除計費資源的完整資訊,請參閱本教學課程最後一個步驟的清除一節。

    應用程式結構

    二進位資料範例結構

    應用程式會使用 Cloud Storage 來儲存二進位資料 (在這個情況下,指的是圖片),同時會繼續針對書籍資訊使用結構化資料庫:Cloud Datastore、Cloud SQL 或 MongoDB。

    瞭解程式碼

    這部分內容會逐步引導您瞭解應用程式程式碼,並說明其運作方式。

    處理使用者上傳

    為了讓使用者上傳圖片,新增/編輯表單會將 enctype 設定為 multipart/form-data。範本動作標記指定了 HTTP POST 要求的目標。對於沒有 ID 的新書籍,目標為 /books。對於現有書籍,目標為 /books/$ID

    <form method="post" enctype="multipart/form-data" action="/books{{if .}}/{{.ID}}{{end}}">

    image 欄位會接受檔案做為其輸入:

    <div class="form-group">
      <label for="image">Cover Image</label>
      <input class="form-control" name="image" id="image" type="file">
    </div>

    上傳至 Cloud Storage

    uploadFileFromForm 函式會檢查 image 表單欄位是否存在,若存在,會將檔案上傳至 Cloud Storage。最後,該函式會傳回上傳檔案的公開網址。

    // uploadFileFromForm uploads a file if it's present in the "image" form field.
    func uploadFileFromForm(r *http.Request) (url string, err error) {
    	f, fh, err := r.FormFile("image")
    	if err == http.ErrMissingFile {
    		return "", nil
    	}
    	if err != nil {
    		return "", err
    	}
    
    	if bookshelf.StorageBucket == nil {
    		return "", errors.New("storage bucket is missing - check config.go")
    	}
    
    	// random filename, retaining existing extension.
    	name := uuid.Must(uuid.NewV4()).String() + path.Ext(fh.Filename)
    
    	ctx := context.Background()
    	w := bookshelf.StorageBucket.Object(name).NewWriter(ctx)
    
    	// Warning: storage.AllUsers gives public read access to anyone.
    	w.ACL = []storage.ACLRule{{Entity: storage.AllUsers, Role: storage.RoleReader}}
    	w.ContentType = fh.Header.Get("Content-Type")
    
    	// Entries are immutable, be aggressive about caching (1 day).
    	w.CacheControl = "public, max-age=86400"
    
    	if _, err := io.Copy(w, f); err != nil {
    		return "", err
    	}
    	if err := w.Close(); err != nil {
    		return "", err
    	}
    
    	const publicURL = "https://storage.googleapis.com/%s/%s"
    	return fmt.Sprintf(publicURL, bookshelf.StorageBucketName, name), nil
    }

    bookFromForm 會呼叫 uploadFileFromForm 函式,進而將所有預期表單值都擷取到 Book 結構中。bookFromForm 函式會將圖片的公開網址做為字串儲存在資料庫中。

    // bookFromForm populates the fields of a Book from form values
    // (see templates/edit.html).
    func bookFromForm(r *http.Request) (*bookshelf.Book, error) {
    	imageURL, err := uploadFileFromForm(r)
    	if err != nil {
    		return nil, fmt.Errorf("could not upload file: %v", err)
    	}
    	if imageURL == "" {
    		imageURL = r.FormValue("imageURL")
    	}
    
    	book := &bookshelf.Book{
    		Title:         r.FormValue("title"),
    		Author:        r.FormValue("author"),
    		PublishedDate: r.FormValue("publishedDate"),
    		ImageURL:      imageURL,
    		Description:   r.FormValue("description"),
    		CreatedBy:     r.FormValue("createdBy"),
    		CreatedByID:   r.FormValue("createdByID"),
    	}

    從 Cloud Storage 提供圖片

    由於系統有圖片的公開網址,因此提供圖片是很簡單的。直接從 Cloud Storage 提供圖片很有幫助,因為要求會利用 Google 的全域提供基礎架構,而且應用程式不必回應圖片的要求,進而釋放了其他要求的 CPU 週期。

    <img src="{{if .ImageURL}}{{.ImageURL}}{{else}}https://placekitten.com/g/200/300{{end}}">
本頁內容對您是否有任何幫助?請提供意見:

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

這個網頁