2
votes

I am developing an Angular App, to display content of a google cloud storage bucket. For the back, I am using google cloud functions in nodeJS

As they mentioned in the documentation to upload a file, I created a function to generate signed url, but when I send my file with the signed url, I got a cors Error in the browser

I tested with postman, it uploads an empty file

Here is my lambda function:

// Imports the Google Cloud client library
const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage();

exports.generateSignedUrl = (req, res) => {
// generate signed url to use for file upload

const filename = req.query.fileName;
console.log('filename ', filename);

const filetype = req.query.fileType;
console.log('filetype ', filetype);

const bucketName = 'nx-terega-omega';

res.set('Access-Control-Allow-Origin', "*");
res.set('Access-Control-Allow-Headers', "Origin, X-Requested-With, 
Content-Type, Accept, Authorization");

if (req.query.fileName !== null && req.query.fileName !== undefined
  && req.query.fileType !== null && req.query.fileType !== undefined) 
{
generateV4UploadSignedUrl(bucketName, filename).then(function (value) 
{
  console.log('File Url response ', value);
  res.status(200).send(JSON.stringify({'url': value}));
}).catch(error => {
  res.status(404).send('Error while generating signed url');
});
} else {
res.status(500).send('Filename not found');
}
};

async function generateV4UploadSignedUrl(bucketName, filename, filetype) {
// [START storage_generate_upload_signed_url_v4]

// These options will allow temporary uploading of the file with outgoing
// Content-Type: application/octet-stream header.
const options = {
version: 'v4',
action: 'write',
expires: Date.now() + 15 * 60 * 1000, // 15 minutes
contentType: filetype,
};

// Get a v4 signed URL for uploading file
const [url] = await storage
.bucket(bucketName)
.file(filename)
.getSignedUrl(options);

console.log('Generated PUT signed URL:');
console.log(url);
console.log('You can use this URL with any user agent, for example:');
console.log("curl -X PUT -H 'Content-Type: application/octet-stream' " +`--upload-file my-file '${url}'`);

return url;
// [END storage_generate_upload_signed_url_v4]
}

When I receive the signed url, I send it my file within, but it returns

No 'Access-Control-Allow-Origin' header is present on the requested resource.
2

2 Answers

4
votes

As mentioned in Brandon Yarbrough response, I had to configure cors in Google Cloud. I was missing something in my configuration

[
 {
  "origin": ["http://example.appspot.com"],
  "responseHeader": ["*"],
  "method": ["GET", "HEAD", "DELETE", "PUT"],
  "maxAgeSeconds": 3600
 }
]

You should include PUT in the method and put * in responseHeader, because Content-Type is not sufficient

1
votes

Signed URLs make use of GCS's XML API. That API allows for cross-origin requests but does not enable it by default. You'll need to specify a CORS policy for your bucket.

For example, you could create a CORS policy like the following (let's say this is a file named policy.json):

[
    {
      "origin": ["http://example.appspot.com"],
      "responseHeader": ["Content-Type"],
      "method": ["GET", "HEAD", "DELETE"],
      "maxAgeSeconds": 3600
    }
]

The full description of a CORS policy document is here: https://cloud.google.com/storage/docs/xml-api/put-bucket-cors#request_body_elements

Now let's apply that policy to a bucket:

gsutil cors set policy.json gs://my-bucket-name

The documentation has more instructions for enabling CORS on a bucket: https://cloud.google.com/storage/docs/configuring-cors