Google Analytics可恢复上传失败

时间:2015-04-23 18:35:22

标签: google-analytics-api

我尝试按照here所述进行可恢复上传。

当我执行上传时,收到的响应状态为400 Bad Request,响应正文为:

{
    "error": {
        "errors": [
            {
                "domain": "global",
                "reason": "badContent",
                "message": "Unsupported content with type: application/octet-stream"
            }
        ],
        "code": 400,
        "message": "Unsupported content with type: application/octet-stream"
    }
}

我用来执行上传的脚本位于Go,这里:

package main

import(
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
    "mime/multipart"
    "net/http"
    "os"
    "path/filepath"
)

func main(){

    // config
    accessToken := "a valid token"
    acctId := "a valid account id"
    webPropertyId := "a valid web property id"
    customDataSourceId := "a valid custom data source id"
    path := "/a/path/to/a/validly/formatted/file.csv"
    params := map[string]string{
        "title":       "My Document",
        "author":      "Becca Petrin",
        "description": "Riveting stuff",
    }
    paramName := "file"
    url := fmt.Sprintf("https://www.googleapis.com/upload/analytics/v3/management/accounts/%s/webproperties/%s/customDataSources/%s/uploads?uploadType=resumable", acctId, webPropertyId, customDataSourceId)

    // create the body
    file, err := os.Open(path)
    if err != nil {
        fmt.Println("Err opening file:", err.Error())
        return
    }
    defer file.Close()

    body := &bytes.Buffer{}
    writer := multipart.NewWriter(body)
    part, err := writer.CreateFormFile(paramName, filepath.Base(path))
    if err != nil {
        fmt.Println("Err creating form file:", err.Error())
        return
    }

    _, err = io.Copy(part, file)
    if err != nil {
        fmt.Println("Err copying:", err.Error())
        return
    }

    for k, v := range params {
        _ = writer.WriteField(k, v)
    }

    if err := writer.Close(); err != nil {
        fmt.Println("Err closing writer:", err.Error())
        return
    }

    req, err := http.NewRequest("POST", url, body)
    if err != nil {
        fmt.Printf("Err creating request:", err.Error())
        return
    }

    // add authorization
    req.Header.Set("Authorization", "Bearer "+accessToken)

    // add headers
    // no multipart headers work, and "application/octet-stream"" doesn't work
    // uncommenting and using "text/plain" results in a 200 without the expected response body
    //req.Header.Add("Content-Type", "text/plain")

    // execute request
    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Err doing request:", err.Error())
        return
    }

    fmt.Println("Response status:", resp.Status)
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("Err reading resp body:", err.Error())
        return
    }
    fmt.Printf("Response body: %s", b)
}

如评论中所述,如果我不包含Content-Type标头,则会收到不受支持的内容类型响应。如果我使用任何多部分内容类型或application / octet-stream,我也会收到它。如果我使用text / plain,我会收到200,但我没有收到预期的身体。

我做错了什么?提前谢谢!

1 个答案:

答案 0 :(得分:0)

我决定采用另一种方法来尝试浏览Google Go客户端库,并让我使用此代码段:

package main


import (

    "errors"
    "fmt"
    "os"
    "time"

    google "google.golang.org/api/analytics/v3"
    "golang.org/x/oauth2"
)

var (
    accessToken = "a valid token"
    acctId = "a valid account ID"
    webPropertyId = "a valid web property ID"
    customDataSourceId = "a valid custom data source ID"
    filePath = "/path/to/file.csv"
)

func main(){

    ctx1 := NewCallContext()
    tokenSource := TokenSource{}
    httpClient := oauth2.NewClient(ctx1, &tokenSource)

    service, err := google.New(httpClient)
    if err != nil {
        fmt.Println("Err making client:", err.Error())
        return
    }

    r, err := NewFileReader(filePath)
    if err != nil {
        fmt.Println("Err making reader:", err.Error())
        return
    }

    ctx2 := NewCallContext()
    call := service.Management.Uploads.UploadData(acctId, webPropertyId, customDataSourceId)
    call = call.ResumableMedia(ctx2, r, 10, "application/octet-stream")

    upload, err := call.Do()
    if err != nil {
        fmt.Println("Err doing call: %v", err)
        return
    }

    fmt.Printf("%s", upload)
}


// http://golang.org/pkg/io/#ReaderAt
func NewFileReader(pathToFile string) (*FileReader, error) {
    f, err := os.Open(pathToFile)
    if err != nil {
        return nil, err
    }
    return &FileReader{f}, nil
}

type FileReader struct {
    f *os.File
}

func (s *FileReader) ReadAt(p []byte, off int64) (int, error) {
    return s.f.ReadAt(p, off)
}

// https://godoc.org/golang.org/x/net/context#Context
func NewCallContext() *CallContext {
    c := make(<-chan struct{})
    return &CallContext{c}
}

type CallContext struct {
    doneChan <-chan struct{}
}

func (s *CallContext) Deadline() (time.Time, bool) {
    return time.Now().Add(time.Duration(10) * time.Second), true
}

func (s *CallContext) Done() <-chan struct{} {
    return s.doneChan
}

func (s *CallContext) Err() error {

    select {
    case <- s.doneChan:
        return errors.New("Done")
    default:
    }
    return nil
}

func (s *CallContext) Value(key interface{}) interface{} {
    return nil
}

// satisfies the oauth2 tokensource interface
type TokenSource struct {}

func (t *TokenSource) Token() (*oauth2.Token, error) {
    return &oauth2.Token{AccessToken:accessToken}, nil
}