是否可以在GAE Golang Blobstore中存储任意数据?

时间:2013-11-22 11:40:59

标签: database google-app-engine go blobstore

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

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

2 个答案:

答案 0 :(得分:3)

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

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

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

另一种方法是以某种方式模拟用户上传。 Go有一个http客户端,可以发送文件上传到网址。尽管如此,这将是一种hacky方式。

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()

答案 1 :(得分:2)

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

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

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

file

现在,如果你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) } ,它会将文件存储在Blobstore中。

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