2014-12-03 79 views
6

獲取奇怪的錯誤。Google Storage API - 可恢復上傳,AJAX,CORS錯誤

當用戶想要上傳文件時,他們會向我的服務器發送一個AJAX請求。我的服務器使用OAuth2向服務器端認證服務器端,創建訪問令牌,開始可恢復的上傳,並將可恢復的上傳URI和訪問令牌傳遞給瀏覽器。

然後,瀏覽器直接上傳到Google Storage。

一切似乎都很好。該文件到達存儲桶沒有問題,但我仍然在Chrome上收到CORS錯誤,我不確定在哪裏或爲什麼。下面是我的javascript代碼的簡化版本:

var file = document.getElementById("fileInput").files[0]; 

var request = requestUpload(); 
var token = request.token; 
var uri = request.uri; 

var r = new XMLHttpRequest(); 
r.open('PUT', uri, true); 
r.setRequestHeader("Authorization", "Bearer "+token); 
r.send(file);` 

相當簡單 - 但我仍然得到這個常見的錯誤:

XMLHttpRequest cannot load https://www.googleapis.com/upload/storage/v1/b/*****. No 'Access-Control-Allow-Origin' header is present on the requested resource.

儘管這似乎是完全正常的事實。 Javascript是否請求查看某些我不被允許的內容?我想不要有錯誤。

XMLHttpRequest對象在所有內容完成上傳後還會觸發錯誤事件。我猜這個請求正在期待Google的反饋,它沒有得到,JavaScript正在成爲JavaScript的拉丁語,所以我發現的大部分討論都是關於jQuery的。

非常感謝!

回答

4

我剛碰到這個確切的問題。如果我撓了一下腦,但我想出了一個解決方案。這是很糟糕記錄,並在行爲並不完全是有意義的,但如果你看看過去的子彈點這個頁面上

https://cloud.google.com/storage/docs/cross-origin

When using the resumable upload protocol, the Origin from the first (start upload) request is always used to decide the Access-Control-Allow-Origin header in the response, even if you use a different Origin for subsequent request. Therefore, you should either use the same origin for the first and subsequent requests, or if the first request has a different origin than subsequent requests, use the XML API with the CORS configuration set to *.

所以我所做的是我加入了Origin: http://example.com請求在來自服務器的可恢復會話發起請求上。這樣,與該上傳會話ID相對應的所有客戶端請求都將檢查該來源。

我仍然覺得奇怪的一件事是OPTIONS由瀏覽器做出的預檢請求(至少對我來說)是允許通過任何東西,即使PUT請求失敗。執行gsutil cors get gs://your-bucket

+0

嗨@克雷格,你有沒有在最後一塊這個問題?如果你這樣做了,你是如何解決它的:http://stackoverflow.com/questions/34832640/google-storage-doesnt-send-cors-headers-for-final-chunk-in-resumable-upload – janesconference 2016-01-16 22:28:37

1

即使使用這樣的配置,則需要根據@Craig的答案發起初始POST請求:

[{"maxAgeSeconds": 3600, "method": ["GET", "PUT", "POST", "HEAD", "DELETE"], "origin": ["*"], "responseHeader": ["*"]}]

使用例如golang很容易將Origin傳遞給gcloud請求。有一個r http.Request輸入,您可以執行r.Header.Get("Origin")並將它傳遞給調用gcloud上POST的函數。這是我在golang寫的一個函數:

func GetUploadURL(bucket, object, contentType, origin string, expires time.Time) (string, error) { 
    url, err := storage.SignedURL(bucket, object, &storage.SignedURLOptions{ 
     GoogleAccessID: "[email protected]", 
     PrivateKey:  pkey, // set this in package init() function 
     Method:   http.MethodPost, 
     Expires:  expires, 
     ContentType: contentType, 
     Headers:  []string{"x-goog-resumable:start"}, 
    }) 

    if err != nil { 
     return "", err 
    } 

    req, err := http.NewRequest("POST", url, nil) 
    if err != nil { 
     return "", err 
    } 
    req.Header.Set("Content-Type", contentType) 
    req.Header.Set("x-goog-resumable", "start") 
    req.Header.Set("Origin", origin) 

    resp, err := http.DefaultClient.Do(req) 
    if err != nil { 
     return "", err 
    } 
    defer resp.Body.Close() 
    return resp.Header.Get("Location"), nil 
}