我尝试按照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,但我没有收到预期的身体。
我做错了什么?提前谢谢!
答案 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
}