我正在Google App Engine Go中创建一个大型数据库应用程序。我的大多数数据都很小,所以我将它们存储在数据存储区中没有问题。但是,我知道我会遇到一些几兆字节的条目,所以我将不得不使用Blobstore来保存它们。
查看at the reference for Blobstore,该服务似乎主要用于上传到服务的文件。我需要调用什么函数来存储Blobstore中的任意数据,就像在Datastore中一样?我已经可以将数据转换为[]字节,我不需要在blob中索引任何内容,只需按ID存储和获取它。
答案 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-data
或multipart/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中。
重要:我不确定如何为您对自己的应用发出的请求收取带宽费用。在最坏的情况下,您将需要为内部流量付费,这比正常带宽更便宜。