【问题标题】:Upload Json Array from GAE to Cloud Storage in GO将 Json 数组从 GAE 上传到 GO 中的云存储
【发布时间】:2015-05-08 12:58:37
【问题描述】:

我尝试将一个 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();
}

发布的消息使用下面发布的代码进行处理。使用此代码,我可以在以用户 ID 命名的云存储桶上创建一个文件夹。我想做的是将整个json数组复制到文件夹中-即下面代码中的变量 f 。我尝试使用 io.Copy(wc, f) 但它给了我以下错误:

不能在参数中使用内容(类型 userData)作为类型 io.Reader io. 复制: userData 没有实现 io.Reader(缺少 Read 方法)

显然我做错了什么,但我还是个新手,我完全被困住了。有人可以帮我吗?

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

【问题讨论】:

    标签: javascript json google-app-engine go google-cloud-storage


    【解决方案1】:

    这段代码非常令人困惑,但据我所知,您是否正在尝试这样做:

    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
        }
    }
    

    您不能将对象直接写入文件,需要先对其进行编码。我假设你想要json。你可以这样做:

    bs, err := json.Marshal(content)
    if err != nil {
      return err
    }
    io.Copy(wc, bytes.NewReader(bs))
    

    但最好使用json.NewEncoder:

    json.NewEncoder(wc).Encode(content)
    

    您的createUserFolder 也可以简化(您不需要缓冲区来连接字符串):

    func (d *saveData) createUserFolder() {
        content := testA(d.r)
        d.createFile(content.id + "/")
    }
    

    我不知道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
    }
    

    使用大写的字段名称,以便 json 可以为您处理转换。

    可能还有很多重构工作要做​​,但也许这已经足够开始了。

    【讨论】:

    • 嗨,Caleb,您的代码运行良好,谢谢!你是对的,代码写得不好,我还在做原型,甚至更多,我还在学习围棋。无论如何,下次我会尝试发布更清晰的代码。非常感谢,很抱歉造成混乱。
    猜你喜欢
    • 2015-05-19
    • 2015-08-11
    • 2014-05-07
    • 2012-07-15
    • 2012-10-15
    • 2017-12-06
    • 2021-10-28
    • 1970-01-01
    • 2020-05-24
    相关资源
    最近更新 更多