I trying to upload to google cloud storage a json array which is posted by an app engine application using the following code:
我尝试使用以下代码将应用引擎应用程序发布的json数组上传到谷歌云存储:
saveData : function saveData() {
var _this = this,
save = this.shadowRoot.querySelector('#save-data'),
subData = JSON.stringify(_this.app.userSession);
save.url="url";
save.body = subData;
save.go();
}
The posted message is handled in go with the code posted below. With this code I'm able to create a folder on the cloud storage bucket which is named with the user ID. What I would love to do is to copy into the folder the entire json array -i.e. the variable f in the code below. I tried with io.Copy(wc, f) but it gives me the following error:
发布的消息将与下面发布的代码一起处理。使用此代码,我可以在云存储桶上创建一个文件夹,该文件夹以用户ID命名。我想要做的是将整个json数组-i.e复制到文件夹中。下面代码中的变量f。我尝试使用io.Copy(wc,f),但它给了我以下错误:
cannot use content (type userData) as type io.Reader in argument to io.Copy: userData does not implement io.Reader (missing Read method)
不能使用内容(类型userData)作为io.Copy参数中的类型io.Reader:userData没有实现io.Reader(缺少Read方法)
Obviously I'm doing something wrong, but I'm quite new to go and I'm totally stuck. Can someone help me?
显然我做错了,但我很新,我完全陷入了困境。有人能帮我吗?
package expt
import (
"bytes"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/appengine"
"google.golang.org/appengine/file"
"google.golang.org/appengine/urlfetch"
"google.golang.org/cloud"
"google.golang.org/cloud/storage"
)
var bucket = "expt"
func init() {
http.HandleFunc("/", handleStatic)
http.HandleFunc("/save", saveJson)
}
func handleStatic(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "no-cache")
http.ServeFile(w, r, "static/"+r.URL.Path)
}
type Result map[string]interface {
}
type userData struct {
id string
age string
gender string
}
func testA(r *http.Request) userData {
defer r.Body.Close()
body, err := ioutil.ReadAll(r.Body)
var userDataCurr userData
if err != nil {
log.Printf("Couldn't read request body: %s", err)
} else {
var f Result
err := json.Unmarshal(body, &f)
if err != nil {
log.Println("Error: %s", err)
} else {
user := f["user"].(map[string]interface{})
userDataCurr.id = user["id"].(string)
}
}
return userDataCurr
}
// saveData struct holds information needed to run the various saving functions.
type saveData struct {
c context.Context
r *http.Request
w http.ResponseWriter
ctx context.Context
// cleanUp is a list of filenames that need cleaning up at the end of the saving.
cleanUp []string
// failed indicates that one or more of the saving steps failed.
failed bool
}
func (d *saveData) errorf(format string, args ...interface{}) {
d.failed = true
// log.Errorf(d.c, format, args...)
}
// testSave is the main saving entry point that calls the GCS operations.
func saveJson(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
if bucket == "" {
var err error
if bucket, err = file.DefaultBucketName(c); err != nil {
// log.Errorf(c, "failed to get default GCS bucket name: %v", err)
return
}
}
hc := &http.Client{
Transport: &oauth2.Transport{
Source: google.AppEngineTokenSource(c, storage.ScopeFullControl),
Base: &urlfetch.Transport{Context: c},
},
}
ctx := cloud.NewContext(appengine.AppID(c), hc)
d := &saveData{
c: c,
r: r,
w: w,
ctx: ctx,
}
d.createUserFolder()
}
// createFile creates a file in Google Cloud Storage.
func (d *saveData) createFile(fileName string) {
wc := storage.NewWriter(d.ctx, bucket, fileName)
wc.ContentType = "text/plain"
d.cleanUp = append(d.cleanUp, fileName)
if err := wc.Close(); err != nil {
d.errorf("createFile: unable to close bucket %q, file %q: %v", bucket, fileName, err)
return
}
}
//create files that will be used by listBucket.
func (d *saveData) createUserFolder() {
var (
content = testA(d.r)
buffer bytes.Buffer
)
buffer.WriteString(content.id)
buffer.WriteString("/")
d.createFile(buffer.String())
}
1 个解决方案
#1
This code is very confusing but from what I can tell are you trying to do this:
这段代码非常令人困惑,但据我所知,你试图这样做:
func (d *saveData) createFile(fileName string, content userData) {
wc := storage.NewWriter(d.ctx, bucket, fileName)
wc.ContentType = "text/plain"
d.cleanUp = append(d.cleanUp, fileName)
//*** new code *******
io.Copy(wc, content)
//********************
if err := wc.Close(); err != nil {
d.errorf("createFile: unable to close bucket %q, file %q: %v", bucket, fileName, err)
return
}
}
You can't write an object directly to a file, it needs to be encoded first. I'm assuming you want json. You could do it this way:
您不能将对象直接写入文件,需要先对其进行编码。我假设你想要json。你可以这样做:
bs, err := json.Marshal(content)
if err != nil {
return err
}
io.Copy(wc, bytes.NewReader(bs))
But it would be better to use json.NewEncoder
:
但是最好使用json.NewEncoder:
json.NewEncoder(wc).Encode(content)
Your createUserFolder
can also be simplified (you don't need a buffer to concatenate strings):
您的createUserFolder也可以简化(您不需要缓冲区来连接字符串):
func (d *saveData) createUserFolder() {
content := testA(d.r)
d.createFile(content.id + "/")
}
I don't know what testA
is supposed to mean, but it too can be simplified:
我不知道testA应该是什么意思,但它也可以简化:
type UserData {
ID string `json:"id"`
Age string `json:"age"`
Gender string `json:"gender"`
}
func testA(r *http.Request) UserData {
defer r.Body.Close()
var obj struct {
User UserData `json:"user"`
}
err := json.NewDecoder(r.Body).Decode(&obj)
if err != nil {
log.Println("Error: %s", err)
}
return obj.User
}
Use uppercase field names so that json can handle the conversion for you.
使用大写字段名称,以便json可以为您处理转换。
There's probably a lot more refactoring to be done, but maybe that's enough to get started.
可能还有很多重构需要完成,但也许这就足以开始了。
#1
This code is very confusing but from what I can tell are you trying to do this:
这段代码非常令人困惑,但据我所知,你试图这样做:
func (d *saveData) createFile(fileName string, content userData) {
wc := storage.NewWriter(d.ctx, bucket, fileName)
wc.ContentType = "text/plain"
d.cleanUp = append(d.cleanUp, fileName)
//*** new code *******
io.Copy(wc, content)
//********************
if err := wc.Close(); err != nil {
d.errorf("createFile: unable to close bucket %q, file %q: %v", bucket, fileName, err)
return
}
}
You can't write an object directly to a file, it needs to be encoded first. I'm assuming you want json. You could do it this way:
您不能将对象直接写入文件,需要先对其进行编码。我假设你想要json。你可以这样做:
bs, err := json.Marshal(content)
if err != nil {
return err
}
io.Copy(wc, bytes.NewReader(bs))
But it would be better to use json.NewEncoder
:
但是最好使用json.NewEncoder:
json.NewEncoder(wc).Encode(content)
Your createUserFolder
can also be simplified (you don't need a buffer to concatenate strings):
您的createUserFolder也可以简化(您不需要缓冲区来连接字符串):
func (d *saveData) createUserFolder() {
content := testA(d.r)
d.createFile(content.id + "/")
}
I don't know what testA
is supposed to mean, but it too can be simplified:
我不知道testA应该是什么意思,但它也可以简化:
type UserData {
ID string `json:"id"`
Age string `json:"age"`
Gender string `json:"gender"`
}
func testA(r *http.Request) UserData {
defer r.Body.Close()
var obj struct {
User UserData `json:"user"`
}
err := json.NewDecoder(r.Body).Decode(&obj)
if err != nil {
log.Println("Error: %s", err)
}
return obj.User
}
Use uppercase field names so that json can handle the conversion for you.
使用大写字段名称,以便json可以为您处理转换。
There's probably a lot more refactoring to be done, but maybe that's enough to get started.
可能还有很多重构需要完成,但也许这就足以开始了。