【问题标题】:Is it possible to store arbitrary data in GAE Golang Blobstore?是否可以在 GAE Golang Blobstore 中存储任意数据?
【发布时间】:2013-12-07 06:52:24
【问题描述】:

我正在 Google App Engine Go 中创建一个大型数据库应用程序。我的大部分数据都很小,因此将它们存储在 Datastore 中没有问题。但是,我知道我会遇到几兆字节大的条目,所以我将不得不使用 Blobstore 来保存它们。

查看at the reference for Blobstore,该服务似乎主要用于上传到该服务的文件。我需要调用哪些函数才能像在 Datastore 中一样在 Blobstore 中存储任意数据?我已经可以将数据转换为 []byte,我不需要在 blob 中索引任何内容,只需按 ID 存储和获取它。

【问题讨论】:

    标签: database google-app-engine go blobstore


    【解决方案1】:

    有两种方法可以将文件写入 blobstore

    一种方法是使用页面末尾记录的已弃用 API 用于 blobstore。他们的示例代码如下。

    他们将改用的方法是将文件存储在 Google 云存储中并通过 blobstore 提供服务。

    另一种方法是以某种方式模拟用户上传。 Go 有一个 http 客户端,可以将要上传的文件发送到网址。不过,那将是一种很老套的方法。

    var k appengine.BlobKey
    w, err := blobstore.Create(c, "application/octet-stream")
    if err != nil {
            return k, err
    }
    _, err = w.Write([]byte("... some data ..."))
    if err != nil {
            return k, err
    }
    err = w.Close()
    if err != nil {
            return k, err
    }
    return w.Key()
    

    【讨论】:

      【解决方案2】:

      正如@yumaikas 所说,Files API 已被弃用。如果此数据来自某种用户上传,则应修改上传表单以使用 Blobstore 上传 URL(特别是,将编码设置为 multipart/form-datamultipart/mixed 并将所有文件上传字段命名为 file,除了您不想存储在 blobstore 中的那些)。

      但是,如果这是不可能的(例如,您无法控制用户输入,或者您必须在将数据存储到 Blobstore 之前对服务器上的数据进行预处理),那么您要么必须使用已弃用的Files API,或使用URLFetch API 上传数据。

      这是一个完整的示例应用程序,它将为您在 Blobstore 中存储一个示例文件。

      package sample
      
      import (
          "bytes"
          "net/http"
          "mime/multipart"
      
          "appengine"
          "appengine/blobstore"
          "appengine/urlfetch"
      )
      
      const SampleData = `foo,bar,spam,eggs`
      
      func init() {
          http.HandleFunc("/test", StoreSomeData)
          http.HandleFunc("/upload", Upload)
      }
      
      func StoreSomeData(w http.ResponseWriter, r *http.Request) {
          c := appengine.NewContext(r)
      
          // First you need to create the upload URL:
          u, err := blobstore.UploadURL(c, "/upload", nil)
          if err != nil {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              c.Errorf("%s", err)
              return
          }
      
          // Now you can prepare a form that you will submit to that URL.
          var b bytes.Buffer
          fw := multipart.NewWriter(&b)
          // Do not change the form field, it must be "file"!
          // You are free to change the filename though, it will be stored in the BlobInfo.
          file, err := fw.CreateFormFile("file", "example.csv")
          if err != nil {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              c.Errorf("%s", err)
              return
          }
          if _, err = file.Write([]byte(SampleData)); err != nil {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              c.Errorf("%s", err)
              return
          }
          // Don't forget to close the multipart writer.
          // If you don't close it, your request will be missing the terminating boundary.
          fw.Close()
      
          // Now that you have a form, you can submit it to your handler.
          req, err := http.NewRequest("POST", u.String(), &b)
          if err != nil {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              c.Errorf("%s", err)
              return
          }
          // Don't forget to set the content type, this will contain the boundary.
          req.Header.Set("Content-Type", fw.FormDataContentType())
      
          // Now submit the request.
          client := urlfetch.Client(c)
          res, err := client.Do(req)
          if err != nil {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              c.Errorf("%s", err)
              return
          }
      
          // Check the response status, it should be whatever you return in the `/upload` handler.
          if res.StatusCode != http.StatusCreated {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              c.Errorf("bad status: %s", res.Status)
              return
          }
          // Everything went fine.
          w.WriteHeader(res.StatusCode)
      }
      
      func Upload(w http.ResponseWriter, r *http.Request) {
          c := appengine.NewContext(r)
      
          // Here we just checked that the upload went through as expected.
          if _, _, err := blobstore.ParseUpload(r); err != nil {
              http.Error(w, err.Error(), http.StatusInternalServerError)
              c.Errorf("%s", err)
              return
          }
          // Everything seems fine. Signal the other handler using the status code.
          w.WriteHeader(http.StatusCreated)
      }
      

      现在如果您curl http://localhost:8080/test,它将在 Blobstore 中存储一个文件。

      重要提示:我不确定您对自己的应用提出的请求将如何收取带宽费用。在最坏的情况下,您需要为内部流量付费,这比普通带宽 iirc 便宜。

      【讨论】:

      • 上传文件的content_type如何设置?它不断作为应用程序/八位字节流上传
      • 对此的简短回答是,如果你想生成和存储内容,现在你应该使用 Cloud Storage,它有更好的 API。
      • @KevinPostal 设置您要用自己的代码替换 CreateFormFile 的内容类型:请参阅golang.org/src/mime/multipart/writer.go?s=3374:3452#L130
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-11
      • 2011-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多