Getting a strange error.
When a user wants to upload a file, they send an AJAX request to my server. My server authenticates server-side with OAuth2 to Google's servers, creates an access token, begins a resumable upload, and passes the resumable upload URI and the access token to the browser.
Then, the browser uploads directly to Google Storage.
Everything seems to be fine. The file gets to the Storage Bucket no problem, but I am still receiving a CORS error on Chrome and I'm not sure where or why. Here is a simplified version of my javascript code:
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);`
Pretty simple- but I'm still getting this common error:
XMLHttpRequest cannot load https://www.googleapis.com/upload/storage/v1/b/*****. No 'Access-Control-Allow-Origin' header is present on the requested resource.
Despite the fact that it seems to be completely functional. Is Javascript requesting to see something I'm not allowed to by some default? I'd like not to have errors.
The XMLHttpRequest object also triggers an error event after everything is finished uploading. I'm guessing the request is expecting some kind of feedback from Google that it isn't getting and JavaScript is becoming the Latin of JavaScript so most of the discussion I've found is regarding jQuery.
Many thanks!
I just ran into this exact issue. Had me scratching my head for a little bit, but I figured out a solution. It is pretty poorly documented, and the behavior doesn't exactly make sense, but if you look at the last bullet point on this page
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 *.
So what I did was I added the Origin: http://example.com
request header on the resumable session initiation request from the server. That way all the client requests that correspond to that upload session id will check against that origin.
The one thing that I still find strange is the OPTIONS
preflight request made by the browser (at least for me) was allowing anything through even though the PUT
request was failing.
Even with this configuration when executing gsutil cors get gs://your-bucket
, you will need to initiate the initial POST request according to @Craig 's answer:
[{"maxAgeSeconds": 3600, "method": ["GET", "PUT", "POST", "HEAD", "DELETE"], "origin": ["*"], "responseHeader": ["*"]}]
.
Using for example golang it is easy to pass the Origin
on to the gcloud request. Having a r http.Request
input, you could execute r.Header.Get("Origin")
and pass it on to a function calling the POST on gcloud. This is a function i wrote doing this in 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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With