Upload Json Array from GAE to Cloud Storage in GO

huangapple go评论83阅读模式
英文:

Upload Json Array from GAE to Cloud Storage in GO

问题

我正在尝试使用以下代码将由App Engine应用程序发布的JSON数组上传到Google Cloud Storage:

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

在上述代码中,使用go函数处理发布的消息。使用下面的代码,我能够在云存储存储桶中创建一个以用户ID命名的文件夹。我希望能够将整个JSON数组(即代码中的变量f)复制到该文件夹中。我尝试使用io.Copy(wc, f),但是它给出了以下错误:

无法将content(类型为userData)作为io.Reader类型的参数传递给io.Copy:
userData未实现io.Reader(缺少Read方法)

显然,我做错了什么,但我对Go语言还很陌生,完全被困住了。有人可以帮助我吗?

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
}

type saveData struct {
	c   context.Context
	r   *http.Request
	w   http.ResponseWriter
	ctx context.Context
	cleanUp []string
	failed bool
}

func (d *saveData) errorf(format string, args ...interface{}) {
	d.failed = true
	// log.Errorf(d.c, format, args...)
}

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

}

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

func (d *saveData) createUserFolder() {
	var (
		content = testA(d.r)
		buffer  bytes.Buffer
	)

	buffer.WriteString(content.id)
	buffer.WriteString("/")
	d.createFile(buffer.String())
}

希望这可以帮助到你!

英文:

I trying to upload to google cloud storage a json array which is posted by an app engine application using the following code:

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:

> cannot use content (type userData) as type io.Reader in argument to
> io.Copy:
> userData does not implement io.Reader (missing Read method)

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

得分: 0

这段代码非常混乱,但从我所能看到的内容来看,你是想要做以下操作:

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)
    
    //*** 新代码 *******
    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 struct {
  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 可以自动进行转换

可能还有很多重构的工作要做但也许这些就足够开始了

<details>
<summary>英文:</summary>

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 = &quot;text/plain&quot;
        d.cleanUp = append(d.cleanUp, fileName)
        
        //*** new code *******
        io.Copy(wc, content)
        //********************

        if err := wc.Close(); err != nil {
            d.errorf(&quot;createFile: unable to close bucket %q, file %q: %v&quot;, bucket, fileName, err)
            return
        }
    }

You can&#39;t write an object directly to a file, it needs to be encoded first. I&#39;m assuming you want json. You could do it this way:

    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(wc).Encode(content)
    
Your `createUserFolder` can also be simplified (you don&#39;t need a buffer to concatenate strings):

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

I don&#39;t know what `testA` is supposed to mean, but it too can be simplified:

    type UserData {
      ID     string `json:&quot;id&quot;`
      Age    string `json:&quot;age&quot;`
      Gender string `json:&quot;gender&quot;`
    }

    func testA(r *http.Request) UserData {
      defer r.Body.Close()
      var obj struct {
        User UserData `json:&quot;user&quot;`
      }
      err := json.NewDecoder(r.Body).Decode(&amp;obj)
      if err != nil {
          log.Println(&quot;Error: %s&quot;, err)
      }
      return obj.User
    }
    
Use uppercase field names so that json can handle the conversion for you.

There&#39;s probably a lot more refactoring to be done, but maybe that&#39;s enough to get started.

</details>



huangapple
  • 本文由 发表于 2015年5月8日 20:58:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/30124515.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定