GAE Golang Blobstore是否能够存储任意数据?

3
我正在使用Google App Engine Go创建一个大型数据库应用程序。我的大多数数据都很小,因此在Datastore中存储它们没有问题。但是,我知道我会遇到一些条目,它们将有几兆字节大,因此我必须使用Blobstore来保存它们。
查看Blobstore的参考文献,似乎该服务主要用于上传到服务的文件。我需要调用哪些函数才能像在Datastore中那样存储任意数据在Blobstore中?我已经可以将数据转换为[]byte,并且我不需要在blob中索引任何内容,只需通过ID存储和提取它即可。
2个回答

3

您可以通过两种方式将文件写入Blobstore。

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

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

另一种方法是模拟用户上传。Go语言有一个HTTP客户端,可以向Web地址发送要上传的文件。虽然这是一种不正规的方法。

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中。
重要提示:我不确定您如何为向自己的应用程序发出的请求计费。在最坏的情况下,您将被收取内部流量费用,这比普通带宽便宜。

你如何设置上传文件的content_type?它一直被上传为application/octet-stream。 - Kevin Postal
简而言之,如果你想生成和存储内容的话,现在最好使用Cloud Storage,它有一个更好的API。 - Attila O.
@KevinPostal 要设置内容类型,您需要将CreateFormFile替换为自己的代码:请参考https://golang.org/src/mime/multipart/writer.go?s=3374:3452#L130 - mblakele

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接