diff --git a/lib/storage/file.js b/lib/storage/file.js index ed210a54af2..d64aa09c725 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -1019,12 +1019,21 @@ File.prototype.getSignedPolicy = function(options, callback) { * link will expire. * @param {string=} options.extensionHeaders - If these headers are used, the * server will check to make sure that the client provides matching values. + * @param {string=} options.promptSaveAs - The filename to prompt the user to + * save the file as when the signed url is accessed. This is ignored if + * options.responseDisposition is set. + * @param {string=} options.responseDisposition - The + * response-content-disposition parameter (http://goo.gl/yMWxQV) of the + * signed url. + * @param {string=} options.responseType - The response-content-type parameter + * of the signed url. * @param {function=} callback - The callback function. * * @example * file.getSignedUrl({ * action: 'read', - * expires: Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14) // 2 weeks. + * expires: Math.round(Date.now() / 1000) + (60 * 60 * 24 * 14), // 2 weeks. + * promptSaveAs: 'filename.ext' * }, function(err, url) {}); */ File.prototype.getSignedUrl = function(options, callback) { @@ -1060,11 +1069,32 @@ File.prototype.getSignedUrl = function(options, callback) { ].join('\n')); var signature = sign.sign(credentials.private_key, 'base64'); + var responseContentType = ''; + if (util.is(options.responseType, 'string')) { + responseContentType = + '&response-content-type=' + + encodeURIComponent(options.responseType); + } + + var responseContentDisposition = ''; + if (util.is(options.promptSaveAs, 'string')) { + responseContentDisposition = + '&response-content-disposition=attachment; filename="' + + encodeURIComponent(options.promptSaveAs) + '"'; + } + if (util.is(options.responseDisposition, 'string')) { + responseContentDisposition = + '&response-content-disposition=' + + encodeURIComponent(options.responseDisposition); + } + callback(null, [ 'https://storage.googleapis.com' + options.resource, '?GoogleAccessId=' + credentials.client_email, '&Expires=' + options.expires, - '&Signature=' + encodeURIComponent(signature) + '&Signature=' + encodeURIComponent(signature), + responseContentType, + responseContentDisposition ].join('')); }); }; diff --git a/test/storage/file.js b/test/storage/file.js index 619b30193bf..9913f3c20e7 100644 --- a/test/storage/file.js +++ b/test/storage/file.js @@ -1350,6 +1350,61 @@ describe('File', function() { }); }); + it('should add response-content-type parameter', function(done) { + var type = 'application/json'; + directoryFile.getSignedUrl({ + action: 'read', + expires: Math.round(Date.now() / 1000) + 5, + responseType: type + }, function(err, signedUrl) { + assert(signedUrl.indexOf(encodeURIComponent(type)) > -1); + done(); + }); + }); + + describe('promptSaveAs', function() { + it('should add response-content-disposition', function(done) { + var disposition = 'attachment; filename="fname.ext"'; + directoryFile.getSignedUrl({ + action: 'read', + expires: Math.round(Date.now() / 1000) + 5, + promptSaveAs: 'fname.ext' + }, function(err, signedUrl) { + assert(signedUrl.indexOf(disposition) > -1); + done(); + }); + }); + }); + + describe('responseDisposition', function() { + it('should add response-content-disposition', function(done) { + var disposition = 'attachment; filename="fname.ext"'; + directoryFile.getSignedUrl({ + action: 'read', + expires: Math.round(Date.now() / 1000) + 5, + responseDisposition: disposition + }, function(err, signedUrl) { + assert(signedUrl.indexOf(encodeURIComponent(disposition)) > -1); + done(); + }); + }); + + it('should ignore promptSaveAs if set', function(done) { + var disposition = 'attachment; filename="fname.ext"'; + var saveAs = 'fname2.ext'; + directoryFile.getSignedUrl({ + action: 'read', + expires: Math.round(Date.now() / 1000) + 5, + promptSaveAs: saveAs, + responseDisposition: disposition + }, function(err, signedUrl) { + assert(signedUrl.indexOf(encodeURIComponent(disposition)) > -1); + assert(signedUrl.indexOf(encodeURIComponent(saveAs)) === -1); + done(); + }); + }); + }); + describe('expires', function() { var nowInSeconds = Math.floor(Date.now() / 1000);