Reputation: 302
I'm trying to upload files to Google Cloud Storage (GCS) from the client browser. For that I have a back-end in node.js request a Signed URL from GCS, which is then transmitted to the front-end client to upload the file. The Signed URL request code is the following:
const options = {
version: "v4",
action: "write",
expires: Date.now() + 15 * 60 * 1000, // 15 minutes
contentType: content_type,
};
// Get a v4 signed URL for uploading file
const [url] = await storage
.bucket(bucketName)
.file(filename)
.getSignedUrl(options)
This is working. The issue comes when I try using the Signed URL on the client. Every request ends up with the following error:
Access to XMLHttpRequest at 'https://storage.googleapis.com/deploynets_models/keras_logo.png?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=urlsigner%40deploynets.iam.gserviceaccount.com%2F20200711%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20200711T192805Z&X-Goog-Expires=900&X-Goog-SignedHeaders=content-type%3Bhost&X-Goog-Signature=82b73435e4759e577e9d3b8056c7c69167fdaac5f0f450381ac616034b4830a7661bdb0951a82bf749a35dc1cf9a8493b761f8993127d53948551d7b33f552d118666dcf8f67f494cfaabf2268d7235e955e1243ce3cd453dcc32552677168ad94c6f1fca0032eb57941a806cc14139915e3cd3efc3585497715a8ad32a1ea0278f2e1165272951ae0733d5c6f77cc427fd7ff69431f74f1f3f0e7779c28c2437d323e13a2c6474283b264ab6dc6a94830b2b26fde8160684839a0c6ea551ca7eff8e2d348e09a8c213a93c0532f6fed1dd167cd9cf3480415c0c35987b27abd03684e088682eb5e89008d33dcbf630b58ea6b86e7d7f6574466aa2daa982566' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I've set the CORS policy on the GCS bucket with the following JSON:
[
{
"origin": ["*"],
"method": ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
"responseHeader": ["*"],
"maxAgeSeconds": 120
}
]
(I've also tried listing specifically the origin as http://localhost:3000 for example)
It should be mentioned that I have got it to work in one specific case: when the upload payload is just a plain string. For some reason it works fine then.
I've looked up online all the similar errors I could but to no avail so far. Any idea?
Upvotes: 3
Views: 4453
Reputation: 33
I was facing the exact same issue and there was a problem with the options that I was passing, try changing the options to
const options = {
action: "write",
expires: Date.now() + 15 * 60 * 1000, // 15 minutes
}
this solution worked for me.
Upvotes: 0
Reputation: 927
Update: Finally got it to work! I had to add contentType: 'application/octet-stream',
and processData: false,
for it to work. Otherwise I was getting the errors:
Access to fetch at 'https://storage.googleapis.com/...' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Uncaught TypeError: Illegal invocation
and Uncaught (in promise) TypeError: Failed to execute 'arrayBuffer' on 'Blob': Illegal invocation
So final AJAX request looks like this (working):
$.ajax({
url: <GCS signed upload URL>,
type: 'PUT',
data: f, //file object
contentType: 'application/octet-stream', //seems to be required
processData: false, //seems to be required
})
I also had to set the bucket CORS using the command:
gsutil cors set gcs_cors.json gs://<my_bucket>
The file gcs_cors.json contents were:
[
{
"origin": ["https://<myapp>.appspot.com", http://localhost:8080"],
"responseHeader": ["Content-Type"],
"method": ["GET", "HEAD", "DELETE", "PUT", "POST"],
"maxAgeSeconds": 120
}
]
Original post: I am not sure how this worked for you (happy it did though!), I have tried using the file directly and using Form Data but it was not working. Uploading to GCS seems to be quite a frustrating process. I was hoping the documentation would have a complete working example (front-end/back-end) but it doesn't seem to be the case.
Upvotes: 1
Reputation: 302
I figured out what was wrong. In the upload code (browser side), I was passing a FormData
object as the data to be uploaded, which for some reason was triggering the CORS error above. I fixed it when I passed the file directly.
So instead of using the following:
var data = new FormData()
data.append('file', event.target.files[0])
I use directly the file in event.target.files[0]
.
Upvotes: 0