file.js 138 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389
  1. // Copyright 2019 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
  15. if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
  16. if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
  17. return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
  18. };
  19. var _File_instances, _File_validateIntegrity;
  20. import { ServiceObject, util, } from './nodejs-common/index.js';
  21. import { promisifyAll } from '@google-cloud/promisify';
  22. import * as crypto from 'crypto';
  23. import * as fs from 'fs';
  24. import mime from 'mime';
  25. import * as resumableUpload from './resumable-upload.js';
  26. import { Writable, pipeline } from 'stream';
  27. import * as zlib from 'zlib';
  28. import { ExceptionMessages, IdempotencyStrategy, } from './storage.js';
  29. import { AvailableServiceObjectMethods, Bucket } from './bucket.js';
  30. import { Acl } from './acl.js';
  31. import { SigningError, URLSigner, } from './signer.js';
  32. import { GCCL_GCS_CMD_KEY, } from './nodejs-common/util.js';
  33. import duplexify from 'duplexify';
  34. import { normalize, objectKeyToLowercase, unicodeJSONStringify, formatAsUTCISO, PassThroughShim, } from './util.js';
  35. import { CRC32C } from './crc32c.js';
  36. import { HashStreamValidator } from './hash-stream-validator.js';
  37. import AsyncRetry from 'async-retry';
  38. export var ActionToHTTPMethod;
  39. (function (ActionToHTTPMethod) {
  40. ActionToHTTPMethod["read"] = "GET";
  41. ActionToHTTPMethod["write"] = "PUT";
  42. ActionToHTTPMethod["delete"] = "DELETE";
  43. ActionToHTTPMethod["resumable"] = "POST";
  44. })(ActionToHTTPMethod || (ActionToHTTPMethod = {}));
  45. /**
  46. * @deprecated - no longer used
  47. */
  48. export const STORAGE_POST_POLICY_BASE_URL = 'https://storage.googleapis.com';
  49. /**
  50. * @private
  51. */
  52. const GS_URL_REGEXP = /^gs:\/\/([a-z0-9_.-]+)\/(.+)$/;
  53. /**
  54. * @private
  55. * This regex will match compressible content types. These are primarily text/*, +json, +text, +xml content types.
  56. * This was based off of mime-db and may periodically need to be updated if new compressible content types become
  57. * standards.
  58. */
  59. const COMPRESSIBLE_MIME_REGEX = new RegExp([
  60. /^text\/|application\/ecmascript|application\/javascript|application\/json/,
  61. /|application\/postscript|application\/rtf|application\/toml|application\/vnd.dart/,
  62. /|application\/vnd.ms-fontobject|application\/wasm|application\/x-httpd-php|application\/x-ns-proxy-autoconfig/,
  63. /|application\/x-sh(?!ockwave-flash)|application\/x-tar|application\/x-virtualbox-hdd|application\/x-virtualbox-ova|application\/x-virtualbox-ovf/,
  64. /|^application\/x-virtualbox-vbox$|application\/x-virtualbox-vdi|application\/x-virtualbox-vhd|application\/x-virtualbox-vmdk/,
  65. /|application\/xml|application\/xml-dtd|font\/otf|font\/ttf|image\/bmp|image\/vnd.adobe.photoshop|image\/vnd.microsoft.icon/,
  66. /|image\/vnd.ms-dds|image\/x-icon|image\/x-ms-bmp|message\/rfc822|model\/gltf-binary|\+json|\+text|\+xml|\+yaml/,
  67. ]
  68. .map(r => r.source)
  69. .join(''), 'i');
  70. export class RequestError extends Error {
  71. }
  72. const SEVEN_DAYS = 7 * 24 * 60 * 60;
  73. const GS_UTIL_URL_REGEX = /(gs):\/\/([a-z0-9_.-]+)\/(.+)/g;
  74. const HTTPS_PUBLIC_URL_REGEX = /(https):\/\/(storage\.googleapis\.com)\/([a-z0-9_.-]+)\/(.+)/g;
  75. export var FileExceptionMessages;
  76. (function (FileExceptionMessages) {
  77. FileExceptionMessages["EXPIRATION_TIME_NA"] = "An expiration time is not available.";
  78. FileExceptionMessages["DESTINATION_NO_NAME"] = "Destination file should have a name.";
  79. FileExceptionMessages["INVALID_VALIDATION_FILE_RANGE"] = "Cannot use validation with file ranges (start/end).";
  80. FileExceptionMessages["MD5_NOT_AVAILABLE"] = "MD5 verification was specified, but is not available for the requested object. MD5 is not available for composite objects.";
  81. FileExceptionMessages["EQUALS_CONDITION_TWO_ELEMENTS"] = "Equals condition must be an array of 2 elements.";
  82. FileExceptionMessages["STARTS_WITH_TWO_ELEMENTS"] = "StartsWith condition must be an array of 2 elements.";
  83. FileExceptionMessages["CONTENT_LENGTH_RANGE_MIN_MAX"] = "ContentLengthRange must have numeric min & max fields.";
  84. FileExceptionMessages["DOWNLOAD_MISMATCH"] = "The downloaded data did not match the data from the server. To be sure the content is the same, you should download the file again.";
  85. FileExceptionMessages["UPLOAD_MISMATCH_DELETE_FAIL"] = "The uploaded data did not match the data from the server.\n As a precaution, we attempted to delete the file, but it was not successful.\n To be sure the content is the same, you should try removing the file manually,\n then uploading the file again.\n \n\nThe delete attempt failed with this message:\n\n ";
  86. FileExceptionMessages["UPLOAD_MISMATCH"] = "The uploaded data did not match the data from the server.\n As a precaution, the file has been deleted.\n To be sure the content is the same, you should try uploading the file again.";
  87. FileExceptionMessages["MD5_RESUMED_UPLOAD"] = "MD5 cannot be used with a continued resumable upload as MD5 cannot be extended from an existing value";
  88. FileExceptionMessages["MISSING_RESUME_CRC32C_FINAL_UPLOAD"] = "The CRC32C is missing for the final portion of a resumed upload, which is required for validation. Please provide `resumeCRC32C` if validation is required, or disable `validation`.";
  89. })(FileExceptionMessages || (FileExceptionMessages = {}));
  90. /**
  91. * A File object is created from your {@link Bucket} object using
  92. * {@link Bucket#file}.
  93. *
  94. * @class
  95. */
  96. class File extends ServiceObject {
  97. /**
  98. * Cloud Storage uses access control lists (ACLs) to manage object and
  99. * bucket access. ACLs are the mechanism you use to share objects with other
  100. * users and allow other users to access your buckets and objects.
  101. *
  102. * An ACL consists of one or more entries, where each entry grants permissions
  103. * to an entity. Permissions define the actions that can be performed against
  104. * an object or bucket (for example, `READ` or `WRITE`); the entity defines
  105. * who the permission applies to (for example, a specific user or group of
  106. * users).
  107. *
  108. * The `acl` object on a File instance provides methods to get you a list of
  109. * the ACLs defined on your bucket, as well as set, update, and delete them.
  110. *
  111. * See {@link http://goo.gl/6qBBPO| About Access Control lists}
  112. *
  113. * @name File#acl
  114. * @mixes Acl
  115. *
  116. * @example
  117. * ```
  118. * const {Storage} = require('@google-cloud/storage');
  119. * const storage = new Storage();
  120. * const myBucket = storage.bucket('my-bucket');
  121. *
  122. * const file = myBucket.file('my-file');
  123. * //-
  124. * // Make a file publicly readable.
  125. * //-
  126. * const options = {
  127. * entity: 'allUsers',
  128. * role: storage.acl.READER_ROLE
  129. * };
  130. *
  131. * file.acl.add(options, function(err, aclObject) {});
  132. *
  133. * //-
  134. * // If the callback is omitted, we'll return a Promise.
  135. * //-
  136. * file.acl.add(options).then(function(data) {
  137. * const aclObject = data[0];
  138. * const apiResponse = data[1];
  139. * });
  140. * ```
  141. */
  142. /**
  143. * The API-formatted resource description of the file.
  144. *
  145. * Note: This is not guaranteed to be up-to-date when accessed. To get the
  146. * latest record, call the `getMetadata()` method.
  147. *
  148. * @name File#metadata
  149. * @type {object}
  150. */
  151. /**
  152. * The file's name.
  153. * @name File#name
  154. * @type {string}
  155. */
  156. /**
  157. * @callback Crc32cGeneratorToStringCallback
  158. * A method returning the CRC32C as a base64-encoded string.
  159. *
  160. * @returns {string}
  161. *
  162. * @example
  163. * Hashing the string 'data' should return 'rth90Q=='
  164. *
  165. * ```js
  166. * const buffer = Buffer.from('data');
  167. * crc32c.update(buffer);
  168. * crc32c.toString(); // 'rth90Q=='
  169. * ```
  170. **/
  171. /**
  172. * @callback Crc32cGeneratorValidateCallback
  173. * A method validating a base64-encoded CRC32C string.
  174. *
  175. * @param {string} [value] base64-encoded CRC32C string to validate
  176. * @returns {boolean}
  177. *
  178. * @example
  179. * Should return `true` if the value matches, `false` otherwise
  180. *
  181. * ```js
  182. * const buffer = Buffer.from('data');
  183. * crc32c.update(buffer);
  184. * crc32c.validate('DkjKuA=='); // false
  185. * crc32c.validate('rth90Q=='); // true
  186. * ```
  187. **/
  188. /**
  189. * @callback Crc32cGeneratorUpdateCallback
  190. * A method for passing `Buffer`s for CRC32C generation.
  191. *
  192. * @param {Buffer} [data] data to update CRC32C value with
  193. * @returns {undefined}
  194. *
  195. * @example
  196. * Hashing buffers from 'some ' and 'text\n'
  197. *
  198. * ```js
  199. * const buffer1 = Buffer.from('some ');
  200. * crc32c.update(buffer1);
  201. *
  202. * const buffer2 = Buffer.from('text\n');
  203. * crc32c.update(buffer2);
  204. *
  205. * crc32c.toString(); // 'DkjKuA=='
  206. * ```
  207. **/
  208. /**
  209. * @typedef {object} CRC32CValidator
  210. * @property {Crc32cGeneratorToStringCallback}
  211. * @property {Crc32cGeneratorValidateCallback}
  212. * @property {Crc32cGeneratorUpdateCallback}
  213. */
  214. /**
  215. * @callback Crc32cGeneratorCallback
  216. * @returns {CRC32CValidator}
  217. */
  218. /**
  219. * @typedef {object} FileOptions Options passed to the File constructor.
  220. * @property {string} [encryptionKey] A custom encryption key.
  221. * @property {number} [generation] Generation to scope the file to.
  222. * @property {string} [kmsKeyName] Cloud KMS Key used to encrypt this
  223. * object, if the object is encrypted by such a key. Limited availability;
  224. * usable only by enabled projects.
  225. * @property {string} [userProject] The ID of the project which will be
  226. * billed for all requests made from File object.
  227. * @property {Crc32cGeneratorCallback} [callback] A function that generates a CRC32C Validator. Defaults to {@link CRC32C}
  228. */
  229. /**
  230. * Constructs a file object.
  231. *
  232. * @param {Bucket} bucket The Bucket instance this file is
  233. * attached to.
  234. * @param {string} name The name of the remote file.
  235. * @param {FileOptions} [options] Configuration options.
  236. * @example
  237. * ```
  238. * const {Storage} = require('@google-cloud/storage');
  239. * const storage = new Storage();
  240. * const myBucket = storage.bucket('my-bucket');
  241. *
  242. * const file = myBucket.file('my-file');
  243. * ```
  244. */
  245. constructor(bucket, name, options = {}) {
  246. var _a, _b;
  247. const requestQueryObject = {};
  248. let generation;
  249. if (options.generation !== null) {
  250. if (typeof options.generation === 'string') {
  251. generation = Number(options.generation);
  252. }
  253. else {
  254. generation = options.generation;
  255. }
  256. if (!isNaN(generation)) {
  257. requestQueryObject.generation = generation;
  258. }
  259. }
  260. Object.assign(requestQueryObject, options.preconditionOpts);
  261. const userProject = options.userProject || bucket.userProject;
  262. if (typeof userProject === 'string') {
  263. requestQueryObject.userProject = userProject;
  264. }
  265. const methods = {
  266. /**
  267. * @typedef {array} DeleteFileResponse
  268. * @property {object} 0 The full API response.
  269. */
  270. /**
  271. * @callback DeleteFileCallback
  272. * @param {?Error} err Request error, if any.
  273. * @param {object} apiResponse The full API response.
  274. */
  275. /**
  276. * Delete the file.
  277. *
  278. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objects/delete| Objects: delete API Documentation}
  279. *
  280. * @method File#delete
  281. * @param {object} [options] Configuration options.
  282. * @param {boolean} [options.ignoreNotFound = false] Ignore an error if
  283. * the file does not exist.
  284. * @param {string} [options.userProject] The ID of the project which will be
  285. * billed for the request.
  286. * @param {DeleteFileCallback} [callback] Callback function.
  287. * @returns {Promise<DeleteFileResponse>}
  288. *
  289. * @example
  290. * ```
  291. * const {Storage} = require('@google-cloud/storage');
  292. * const storage = new Storage();
  293. * const myBucket = storage.bucket('my-bucket');
  294. *
  295. * const file = myBucket.file('my-file');
  296. * file.delete(function(err, apiResponse) {});
  297. *
  298. * //-
  299. * // If the callback is omitted, we'll return a Promise.
  300. * //-
  301. * file.delete().then(function(data) {
  302. * const apiResponse = data[0];
  303. * });
  304. *
  305. * ```
  306. * @example <caption>include:samples/files.js</caption>
  307. * region_tag:storage_delete_file
  308. * Another example:
  309. */
  310. delete: {
  311. reqOpts: {
  312. qs: requestQueryObject,
  313. },
  314. },
  315. /**
  316. * @typedef {array} FileExistsResponse
  317. * @property {boolean} 0 Whether the {@link File} exists.
  318. */
  319. /**
  320. * @callback FileExistsCallback
  321. * @param {?Error} err Request error, if any.
  322. * @param {boolean} exists Whether the {@link File} exists.
  323. */
  324. /**
  325. * Check if the file exists.
  326. *
  327. * @method File#exists
  328. * @param {options} [options] Configuration options.
  329. * @param {string} [options.userProject] The ID of the project which will be
  330. * billed for the request.
  331. * @param {FileExistsCallback} [callback] Callback function.
  332. * @returns {Promise<FileExistsResponse>}
  333. *
  334. * @example
  335. * ```
  336. * const {Storage} = require('@google-cloud/storage');
  337. * const storage = new Storage();
  338. * const myBucket = storage.bucket('my-bucket');
  339. *
  340. * const file = myBucket.file('my-file');
  341. *
  342. * file.exists(function(err, exists) {});
  343. *
  344. * //-
  345. * // If the callback is omitted, we'll return a Promise.
  346. * //-
  347. * file.exists().then(function(data) {
  348. * const exists = data[0];
  349. * });
  350. * ```
  351. */
  352. exists: {
  353. reqOpts: {
  354. qs: requestQueryObject,
  355. },
  356. },
  357. /**
  358. * @typedef {array} GetFileResponse
  359. * @property {File} 0 The {@link File}.
  360. * @property {object} 1 The full API response.
  361. */
  362. /**
  363. * @callback GetFileCallback
  364. * @param {?Error} err Request error, if any.
  365. * @param {File} file The {@link File}.
  366. * @param {object} apiResponse The full API response.
  367. */
  368. /**
  369. * Get a file object and its metadata if it exists.
  370. *
  371. * @method File#get
  372. * @param {options} [options] Configuration options.
  373. * @param {string} [options.userProject] The ID of the project which will be
  374. * billed for the request.
  375. * @param {number} [options.generation] The generation number to get
  376. * @param {string} [options.restoreToken] If this is a soft-deleted object in an HNS-enabled bucket, returns the restore token which will
  377. * be necessary to restore it if there's a name conflict with another object.
  378. * @param {boolean} [options.softDeleted] If true, returns the soft-deleted object.
  379. Object `generation` is required if `softDeleted` is set to True.
  380. * @param {GetFileCallback} [callback] Callback function.
  381. * @returns {Promise<GetFileResponse>}
  382. *
  383. * @example
  384. * ```
  385. * const {Storage} = require('@google-cloud/storage');
  386. * const storage = new Storage();
  387. * const myBucket = storage.bucket('my-bucket');
  388. *
  389. * const file = myBucket.file('my-file');
  390. *
  391. * file.get(function(err, file, apiResponse) {
  392. * // file.metadata` has been populated.
  393. * });
  394. *
  395. * //-
  396. * // If the callback is omitted, we'll return a Promise.
  397. * //-
  398. * file.get().then(function(data) {
  399. * const file = data[0];
  400. * const apiResponse = data[1];
  401. * });
  402. * ```
  403. */
  404. get: {
  405. reqOpts: {
  406. qs: requestQueryObject,
  407. },
  408. },
  409. /**
  410. * @typedef {array} GetFileMetadataResponse
  411. * @property {object} 0 The {@link File} metadata.
  412. * @property {object} 1 The full API response.
  413. */
  414. /**
  415. * @callback GetFileMetadataCallback
  416. * @param {?Error} err Request error, if any.
  417. * @param {object} metadata The {@link File} metadata.
  418. * @param {object} apiResponse The full API response.
  419. */
  420. /**
  421. * Get the file's metadata.
  422. *
  423. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objects/get| Objects: get API Documentation}
  424. *
  425. * @method File#getMetadata
  426. * @param {object} [options] Configuration options.
  427. * @param {string} [options.userProject] The ID of the project which will be
  428. * billed for the request.
  429. * @param {GetFileMetadataCallback} [callback] Callback function.
  430. * @returns {Promise<GetFileMetadataResponse>}
  431. *
  432. * @example
  433. * ```
  434. * const {Storage} = require('@google-cloud/storage');
  435. * const storage = new Storage();
  436. * const myBucket = storage.bucket('my-bucket');
  437. *
  438. * const file = myBucket.file('my-file');
  439. *
  440. * file.getMetadata(function(err, metadata, apiResponse) {});
  441. *
  442. * //-
  443. * // If the callback is omitted, we'll return a Promise.
  444. * //-
  445. * file.getMetadata().then(function(data) {
  446. * const metadata = data[0];
  447. * const apiResponse = data[1];
  448. * });
  449. *
  450. * ```
  451. * @example <caption>include:samples/files.js</caption>
  452. * region_tag:storage_get_metadata
  453. * Another example:
  454. */
  455. getMetadata: {
  456. reqOpts: {
  457. qs: requestQueryObject,
  458. },
  459. },
  460. /**
  461. * @typedef {object} SetFileMetadataOptions Configuration options for File#setMetadata().
  462. * @param {string} [userProject] The ID of the project which will be billed for the request.
  463. */
  464. /**
  465. * @callback SetFileMetadataCallback
  466. * @param {?Error} err Request error, if any.
  467. * @param {object} apiResponse The full API response.
  468. */
  469. /**
  470. * @typedef {array} SetFileMetadataResponse
  471. * @property {object} 0 The full API response.
  472. */
  473. /**
  474. * Merge the given metadata with the current remote file's metadata. This
  475. * will set metadata if it was previously unset or update previously set
  476. * metadata. To unset previously set metadata, set its value to null.
  477. *
  478. * You can set custom key/value pairs in the metadata key of the given
  479. * object, however the other properties outside of this object must adhere
  480. * to the {@link https://goo.gl/BOnnCK| official API documentation}.
  481. *
  482. *
  483. * See the examples below for more information.
  484. *
  485. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objects/patch| Objects: patch API Documentation}
  486. *
  487. * @method File#setMetadata
  488. * @param {object} [metadata] The metadata you wish to update.
  489. * @param {SetFileMetadataOptions} [options] Configuration options.
  490. * @param {SetFileMetadataCallback} [callback] Callback function.
  491. * @returns {Promise<SetFileMetadataResponse>}
  492. *
  493. * @example
  494. * ```
  495. * const {Storage} = require('@google-cloud/storage');
  496. * const storage = new Storage();
  497. * const myBucket = storage.bucket('my-bucket');
  498. *
  499. * const file = myBucket.file('my-file');
  500. *
  501. * const metadata = {
  502. * contentType: 'application/x-font-ttf',
  503. * metadata: {
  504. * my: 'custom',
  505. * properties: 'go here'
  506. * }
  507. * };
  508. *
  509. * file.setMetadata(metadata, function(err, apiResponse) {});
  510. *
  511. * // Assuming current metadata = { hello: 'world', unsetMe: 'will do' }
  512. * file.setMetadata({
  513. * metadata: {
  514. * abc: '123', // will be set.
  515. * unsetMe: null, // will be unset (deleted).
  516. * hello: 'goodbye' // will be updated from 'world' to 'goodbye'.
  517. * }
  518. * }, function(err, apiResponse) {
  519. * // metadata should now be { abc: '123', hello: 'goodbye' }
  520. * });
  521. *
  522. * //-
  523. * // Set a temporary hold on this file from its bucket's retention period
  524. * // configuration.
  525. * //
  526. * file.setMetadata({
  527. * temporaryHold: true
  528. * }, function(err, apiResponse) {});
  529. *
  530. * //-
  531. * // Alternatively, you may set a temporary hold. This will follow the
  532. * // same behavior as an event-based hold, with the exception that the
  533. * // bucket's retention policy will not renew for this file from the time
  534. * // the hold is released.
  535. * //-
  536. * file.setMetadata({
  537. * eventBasedHold: true
  538. * }, function(err, apiResponse) {});
  539. *
  540. * //-
  541. * // If the callback is omitted, we'll return a Promise.
  542. * //-
  543. * file.setMetadata(metadata).then(function(data) {
  544. * const apiResponse = data[0];
  545. * });
  546. * ```
  547. */
  548. setMetadata: {
  549. reqOpts: {
  550. qs: requestQueryObject,
  551. },
  552. },
  553. };
  554. super({
  555. parent: bucket,
  556. baseUrl: '/o',
  557. id: encodeURIComponent(name),
  558. methods,
  559. });
  560. _File_instances.add(this);
  561. this.bucket = bucket;
  562. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  563. this.storage = bucket.parent;
  564. // @TODO Can this duplicate code from above be avoided?
  565. if (options.generation !== null) {
  566. let generation;
  567. if (typeof options.generation === 'string') {
  568. generation = Number(options.generation);
  569. }
  570. else {
  571. generation = options.generation;
  572. }
  573. if (!isNaN(generation)) {
  574. this.generation = generation;
  575. }
  576. }
  577. this.kmsKeyName = options.kmsKeyName;
  578. this.userProject = userProject;
  579. this.name = name;
  580. if (options.encryptionKey) {
  581. this.setEncryptionKey(options.encryptionKey);
  582. }
  583. this.acl = new Acl({
  584. request: this.request.bind(this),
  585. pathPrefix: '/acl',
  586. });
  587. this.crc32cGenerator =
  588. options.crc32cGenerator || this.bucket.crc32cGenerator;
  589. this.instanceRetryValue = (_b = (_a = this.storage) === null || _a === void 0 ? void 0 : _a.retryOptions) === null || _b === void 0 ? void 0 : _b.autoRetry;
  590. this.instancePreconditionOpts = options === null || options === void 0 ? void 0 : options.preconditionOpts;
  591. }
  592. /**
  593. * The object's Cloud Storage URI (`gs://`)
  594. *
  595. * @example
  596. * ```ts
  597. * const {Storage} = require('@google-cloud/storage');
  598. * const storage = new Storage();
  599. * const bucket = storage.bucket('my-bucket');
  600. * const file = bucket.file('image.png');
  601. *
  602. * // `gs://my-bucket/image.png`
  603. * const href = file.cloudStorageURI.href;
  604. * ```
  605. */
  606. get cloudStorageURI() {
  607. const uri = this.bucket.cloudStorageURI;
  608. uri.pathname = this.name;
  609. return uri;
  610. }
  611. /**
  612. * A helper method for determining if a request should be retried based on preconditions.
  613. * This should only be used for methods where the idempotency is determined by
  614. * `ifGenerationMatch`
  615. * @private
  616. *
  617. * A request should not be retried under the following conditions:
  618. * - if precondition option `ifGenerationMatch` is not set OR
  619. * - if `idempotencyStrategy` is set to `RetryNever`
  620. */
  621. shouldRetryBasedOnPreconditionAndIdempotencyStrat(options) {
  622. var _a;
  623. return !(((options === null || options === void 0 ? void 0 : options.ifGenerationMatch) === undefined &&
  624. ((_a = this.instancePreconditionOpts) === null || _a === void 0 ? void 0 : _a.ifGenerationMatch) === undefined &&
  625. this.storage.retryOptions.idempotencyStrategy ===
  626. IdempotencyStrategy.RetryConditional) ||
  627. this.storage.retryOptions.idempotencyStrategy ===
  628. IdempotencyStrategy.RetryNever);
  629. }
  630. /**
  631. * @typedef {array} CopyResponse
  632. * @property {File} 0 The copied {@link File}.
  633. * @property {object} 1 The full API response.
  634. */
  635. /**
  636. * @callback CopyCallback
  637. * @param {?Error} err Request error, if any.
  638. * @param {File} copiedFile The copied {@link File}.
  639. * @param {object} apiResponse The full API response.
  640. */
  641. /**
  642. * @typedef {object} CopyOptions Configuration options for File#copy(). See an
  643. * {@link https://cloud.google.com/storage/docs/json_api/v1/objects#resource| Object resource}.
  644. * @property {string} [cacheControl] The cacheControl setting for the new file.
  645. * @property {string} [contentEncoding] The contentEncoding setting for the new file.
  646. * @property {string} [contentType] The contentType setting for the new file.
  647. * @property {string} [destinationKmsKeyName] Resource name of the Cloud
  648. * KMS key, of the form
  649. * `projects/my-project/locations/location/keyRings/my-kr/cryptoKeys/my-key`,
  650. * that will be used to encrypt the object. Overwrites the object
  651. * metadata's `kms_key_name` value, if any.
  652. * @property {Metadata} [metadata] Metadata to specify on the copied file.
  653. * @property {string} [predefinedAcl] Set the ACL for the new file.
  654. * @property {string} [token] A previously-returned `rewriteToken` from an
  655. * unfinished rewrite request.
  656. * @property {string} [userProject] The ID of the project which will be
  657. * billed for the request.
  658. */
  659. /**
  660. * Copy this file to another file. By default, this will copy the file to the
  661. * same bucket, but you can choose to copy it to another Bucket by providing
  662. * a Bucket or File object or a URL starting with "gs://".
  663. * The generation of the file will not be preserved.
  664. *
  665. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objects/rewrite| Objects: rewrite API Documentation}
  666. *
  667. * @throws {Error} If the destination file is not provided.
  668. *
  669. * @param {string|Bucket|File} destination Destination file.
  670. * @param {CopyOptions} [options] Configuration options. See an
  671. * @param {CopyCallback} [callback] Callback function.
  672. * @returns {Promise<CopyResponse>}
  673. *
  674. * @example
  675. * ```
  676. * const {Storage} = require('@google-cloud/storage');
  677. * const storage = new Storage();
  678. *
  679. * //-
  680. * // You can pass in a variety of types for the destination.
  681. * //
  682. * // For all of the below examples, assume we are working with the following
  683. * // Bucket and File objects.
  684. * //-
  685. * const bucket = storage.bucket('my-bucket');
  686. * const file = bucket.file('my-image.png');
  687. *
  688. * //-
  689. * // If you pass in a string for the destination, the file is copied to its
  690. * // current bucket, under the new name provided.
  691. * //-
  692. * file.copy('my-image-copy.png', function(err, copiedFile, apiResponse) {
  693. * // `my-bucket` now contains:
  694. * // - "my-image.png"
  695. * // - "my-image-copy.png"
  696. *
  697. * // `copiedFile` is an instance of a File object that refers to your new
  698. * // file.
  699. * });
  700. *
  701. * //-
  702. * // If you pass in a string starting with "gs://" for the destination, the
  703. * // file is copied to the other bucket and under the new name provided.
  704. * //-
  705. * const newLocation = 'gs://another-bucket/my-image-copy.png';
  706. * file.copy(newLocation, function(err, copiedFile, apiResponse) {
  707. * // `my-bucket` still contains:
  708. * // - "my-image.png"
  709. * //
  710. * // `another-bucket` now contains:
  711. * // - "my-image-copy.png"
  712. *
  713. * // `copiedFile` is an instance of a File object that refers to your new
  714. * // file.
  715. * });
  716. *
  717. * //-
  718. * // If you pass in a Bucket object, the file will be copied to that bucket
  719. * // using the same name.
  720. * //-
  721. * const anotherBucket = storage.bucket('another-bucket');
  722. * file.copy(anotherBucket, function(err, copiedFile, apiResponse) {
  723. * // `my-bucket` still contains:
  724. * // - "my-image.png"
  725. * //
  726. * // `another-bucket` now contains:
  727. * // - "my-image.png"
  728. *
  729. * // `copiedFile` is an instance of a File object that refers to your new
  730. * // file.
  731. * });
  732. *
  733. * //-
  734. * // If you pass in a File object, you have complete control over the new
  735. * // bucket and filename.
  736. * //-
  737. * const anotherFile = anotherBucket.file('my-awesome-image.png');
  738. * file.copy(anotherFile, function(err, copiedFile, apiResponse) {
  739. * // `my-bucket` still contains:
  740. * // - "my-image.png"
  741. * //
  742. * // `another-bucket` now contains:
  743. * // - "my-awesome-image.png"
  744. *
  745. * // Note:
  746. * // The `copiedFile` parameter is equal to `anotherFile`.
  747. * });
  748. *
  749. * //-
  750. * // If the callback is omitted, we'll return a Promise.
  751. * //-
  752. * file.copy(newLocation).then(function(data) {
  753. * const newFile = data[0];
  754. * const apiResponse = data[1];
  755. * });
  756. *
  757. * ```
  758. * @example <caption>include:samples/files.js</caption>
  759. * region_tag:storage_copy_file
  760. * Another example:
  761. */
  762. copy(destination, optionsOrCallback, callback) {
  763. var _a, _b;
  764. const noDestinationError = new Error(FileExceptionMessages.DESTINATION_NO_NAME);
  765. if (!destination) {
  766. throw noDestinationError;
  767. }
  768. let options = {};
  769. if (typeof optionsOrCallback === 'function') {
  770. callback = optionsOrCallback;
  771. }
  772. else if (optionsOrCallback) {
  773. options = { ...optionsOrCallback };
  774. }
  775. callback = callback || util.noop;
  776. let destBucket;
  777. let destName;
  778. let newFile;
  779. if (typeof destination === 'string') {
  780. const parsedDestination = GS_URL_REGEXP.exec(destination);
  781. if (parsedDestination !== null && parsedDestination.length === 3) {
  782. destBucket = this.storage.bucket(parsedDestination[1]);
  783. destName = parsedDestination[2];
  784. }
  785. else {
  786. destBucket = this.bucket;
  787. destName = destination;
  788. }
  789. }
  790. else if (destination instanceof Bucket) {
  791. destBucket = destination;
  792. destName = this.name;
  793. }
  794. else if (destination instanceof File) {
  795. destBucket = destination.bucket;
  796. destName = destination.name;
  797. newFile = destination;
  798. }
  799. else {
  800. throw noDestinationError;
  801. }
  802. const query = {};
  803. if (this.generation !== undefined) {
  804. query.sourceGeneration = this.generation;
  805. }
  806. if (options.token !== undefined) {
  807. query.rewriteToken = options.token;
  808. }
  809. if (options.userProject !== undefined) {
  810. query.userProject = options.userProject;
  811. delete options.userProject;
  812. }
  813. if (options.predefinedAcl !== undefined) {
  814. query.destinationPredefinedAcl = options.predefinedAcl;
  815. delete options.predefinedAcl;
  816. }
  817. newFile = newFile || destBucket.file(destName);
  818. const headers = {};
  819. if (this.encryptionKey !== undefined) {
  820. headers['x-goog-copy-source-encryption-algorithm'] = 'AES256';
  821. headers['x-goog-copy-source-encryption-key'] = this.encryptionKeyBase64;
  822. headers['x-goog-copy-source-encryption-key-sha256'] =
  823. this.encryptionKeyHash;
  824. }
  825. if (newFile.encryptionKey !== undefined) {
  826. this.setEncryptionKey(newFile.encryptionKey);
  827. }
  828. else if (options.destinationKmsKeyName !== undefined) {
  829. query.destinationKmsKeyName = options.destinationKmsKeyName;
  830. delete options.destinationKmsKeyName;
  831. }
  832. else if (newFile.kmsKeyName !== undefined) {
  833. query.destinationKmsKeyName = newFile.kmsKeyName;
  834. }
  835. if (query.destinationKmsKeyName) {
  836. this.kmsKeyName = query.destinationKmsKeyName;
  837. const keyIndex = this.interceptors.indexOf(this.encryptionKeyInterceptor);
  838. if (keyIndex > -1) {
  839. this.interceptors.splice(keyIndex, 1);
  840. }
  841. }
  842. if (!this.shouldRetryBasedOnPreconditionAndIdempotencyStrat(options === null || options === void 0 ? void 0 : options.preconditionOpts)) {
  843. this.storage.retryOptions.autoRetry = false;
  844. }
  845. if (((_a = options.preconditionOpts) === null || _a === void 0 ? void 0 : _a.ifGenerationMatch) !== undefined) {
  846. query.ifGenerationMatch = (_b = options.preconditionOpts) === null || _b === void 0 ? void 0 : _b.ifGenerationMatch;
  847. delete options.preconditionOpts;
  848. }
  849. this.request({
  850. method: 'POST',
  851. uri: `/rewriteTo/b/${destBucket.name}/o/${encodeURIComponent(newFile.name)}`,
  852. qs: query,
  853. json: options,
  854. headers,
  855. }, (err, resp) => {
  856. this.storage.retryOptions.autoRetry = this.instanceRetryValue;
  857. if (err) {
  858. callback(err, null, resp);
  859. return;
  860. }
  861. if (resp.rewriteToken) {
  862. const options = {
  863. token: resp.rewriteToken,
  864. };
  865. if (query.userProject) {
  866. options.userProject = query.userProject;
  867. }
  868. if (query.destinationKmsKeyName) {
  869. options.destinationKmsKeyName = query.destinationKmsKeyName;
  870. }
  871. this.copy(newFile, options, callback);
  872. return;
  873. }
  874. callback(null, newFile, resp);
  875. });
  876. }
  877. /**
  878. * @typedef {object} CreateReadStreamOptions Configuration options for File#createReadStream.
  879. * @property {string} [userProject] The ID of the project which will be
  880. * billed for the request.
  881. * @property {string|boolean} [validation] Possible values: `"md5"`,
  882. * `"crc32c"`, or `false`. By default, data integrity is validated with a
  883. * CRC32c checksum. You may use MD5 if preferred, but that hash is not
  884. * supported for composite objects. An error will be raised if MD5 is
  885. * specified but is not available. You may also choose to skip validation
  886. * completely, however this is **not recommended**.
  887. * @property {number} [start] A byte offset to begin the file's download
  888. * from. Default is 0. NOTE: Byte ranges are inclusive; that is,
  889. * `options.start = 0` and `options.end = 999` represent the first 1000
  890. * bytes in a file or object. NOTE: when specifying a byte range, data
  891. * integrity is not available.
  892. * @property {number} [end] A byte offset to stop reading the file at.
  893. * NOTE: Byte ranges are inclusive; that is, `options.start = 0` and
  894. * `options.end = 999` represent the first 1000 bytes in a file or object.
  895. * NOTE: when specifying a byte range, data integrity is not available.
  896. * @property {boolean} [decompress=true] Disable auto decompression of the
  897. * received data. By default this option is set to `true`.
  898. * Applicable in cases where the data was uploaded with
  899. * `gzip: true` option. See {@link File#createWriteStream}.
  900. */
  901. /**
  902. * Create a readable stream to read the contents of the remote file. It can be
  903. * piped to a writable stream or listened to for 'data' events to read a
  904. * file's contents.
  905. *
  906. * In the unlikely event there is a mismatch between what you downloaded and
  907. * the version in your Bucket, your error handler will receive an error with
  908. * code "CONTENT_DOWNLOAD_MISMATCH". If you receive this error, the best
  909. * recourse is to try downloading the file again.
  910. *
  911. * NOTE: Readable streams will emit the `end` event when the file is fully
  912. * downloaded.
  913. *
  914. * @param {CreateReadStreamOptions} [options] Configuration options.
  915. * @returns {ReadableStream}
  916. *
  917. * @example
  918. * ```
  919. * //-
  920. * // <h4>Downloading a File</h4>
  921. * //
  922. * // The example below demonstrates how we can reference a remote file, then
  923. * // pipe its contents to a local file. This is effectively creating a local
  924. * // backup of your remote data.
  925. * //-
  926. * const {Storage} = require('@google-cloud/storage');
  927. * const storage = new Storage();
  928. * const bucket = storage.bucket('my-bucket');
  929. *
  930. * const fs = require('fs');
  931. * const remoteFile = bucket.file('image.png');
  932. * const localFilename = '/Users/stephen/Photos/image.png';
  933. *
  934. * remoteFile.createReadStream()
  935. * .on('error', function(err) {})
  936. * .on('response', function(response) {
  937. * // Server connected and responded with the specified status and headers.
  938. * })
  939. * .on('end', function() {
  940. * // The file is fully downloaded.
  941. * })
  942. * .pipe(fs.createWriteStream(localFilename));
  943. *
  944. * //-
  945. * // To limit the downloaded data to only a byte range, pass an options
  946. * // object.
  947. * //-
  948. * const logFile = myBucket.file('access_log');
  949. * logFile.createReadStream({
  950. * start: 10000,
  951. * end: 20000
  952. * })
  953. * .on('error', function(err) {})
  954. * .pipe(fs.createWriteStream('/Users/stephen/logfile.txt'));
  955. *
  956. * //-
  957. * // To read a tail byte range, specify only `options.end` as a negative
  958. * // number.
  959. * //-
  960. * const logFile = myBucket.file('access_log');
  961. * logFile.createReadStream({
  962. * end: -100
  963. * })
  964. * .on('error', function(err) {})
  965. * .pipe(fs.createWriteStream('/Users/stephen/logfile.txt'));
  966. * ```
  967. */
  968. createReadStream(options = {}) {
  969. options = Object.assign({ decompress: true }, options);
  970. const rangeRequest = typeof options.start === 'number' || typeof options.end === 'number';
  971. const tailRequest = options.end < 0;
  972. let validateStream = undefined;
  973. let request = undefined;
  974. const throughStream = new PassThroughShim();
  975. let crc32c = true;
  976. let md5 = false;
  977. if (typeof options.validation === 'string') {
  978. const value = options.validation.toLowerCase().trim();
  979. crc32c = value === 'crc32c';
  980. md5 = value === 'md5';
  981. }
  982. else if (options.validation === false) {
  983. crc32c = false;
  984. }
  985. const shouldRunValidation = !rangeRequest && (crc32c || md5);
  986. if (rangeRequest) {
  987. if (typeof options.validation === 'string' ||
  988. options.validation === true) {
  989. throw new Error(FileExceptionMessages.INVALID_VALIDATION_FILE_RANGE);
  990. }
  991. // Range requests can't receive data integrity checks.
  992. crc32c = false;
  993. md5 = false;
  994. }
  995. const onComplete = (err) => {
  996. if (err) {
  997. // There is an issue with node-fetch 2.x that if the stream errors the underlying socket connection is not closed.
  998. // This causes a memory leak, so cleanup the sockets manually here by destroying the agent.
  999. if (request === null || request === void 0 ? void 0 : request.agent) {
  1000. request.agent.destroy();
  1001. }
  1002. throughStream.destroy(err);
  1003. }
  1004. };
  1005. // We listen to the response event from the request stream so that we
  1006. // can...
  1007. //
  1008. // 1) Intercept any data from going to the user if an error occurred.
  1009. // 2) Calculate the hashes from the http.IncomingMessage response
  1010. // stream,
  1011. // which will return the bytes from the source without decompressing
  1012. // gzip'd content. We then send it through decompressed, if
  1013. // applicable, to the user.
  1014. const onResponse = (err, _body, rawResponseStream) => {
  1015. if (err) {
  1016. // Get error message from the body.
  1017. this.getBufferFromReadable(rawResponseStream).then(body => {
  1018. err.message = body.toString('utf8');
  1019. throughStream.destroy(err);
  1020. });
  1021. return;
  1022. }
  1023. request = rawResponseStream.request;
  1024. const headers = rawResponseStream.toJSON().headers;
  1025. const isCompressed = headers['content-encoding'] === 'gzip';
  1026. const hashes = {};
  1027. // The object is safe to validate if:
  1028. // 1. It was stored gzip and returned to us gzip OR
  1029. // 2. It was never stored as gzip
  1030. const safeToValidate = (headers['x-goog-stored-content-encoding'] === 'gzip' &&
  1031. isCompressed) ||
  1032. headers['x-goog-stored-content-encoding'] === 'identity';
  1033. const transformStreams = [];
  1034. if (shouldRunValidation) {
  1035. // The x-goog-hash header should be set with a crc32c and md5 hash.
  1036. // ex: headers['x-goog-hash'] = 'crc32c=xxxx,md5=xxxx'
  1037. if (typeof headers['x-goog-hash'] === 'string') {
  1038. headers['x-goog-hash']
  1039. .split(',')
  1040. .forEach((hashKeyValPair) => {
  1041. const delimiterIndex = hashKeyValPair.indexOf('=');
  1042. const hashType = hashKeyValPair.substring(0, delimiterIndex);
  1043. const hashValue = hashKeyValPair.substring(delimiterIndex + 1);
  1044. hashes[hashType] = hashValue;
  1045. });
  1046. }
  1047. validateStream = new HashStreamValidator({
  1048. crc32c,
  1049. md5,
  1050. crc32cGenerator: this.crc32cGenerator,
  1051. crc32cExpected: hashes.crc32c,
  1052. md5Expected: hashes.md5,
  1053. });
  1054. }
  1055. if (md5 && !hashes.md5) {
  1056. const hashError = new RequestError(FileExceptionMessages.MD5_NOT_AVAILABLE);
  1057. hashError.code = 'MD5_NOT_AVAILABLE';
  1058. throughStream.destroy(hashError);
  1059. return;
  1060. }
  1061. if (safeToValidate && shouldRunValidation && validateStream) {
  1062. transformStreams.push(validateStream);
  1063. }
  1064. if (isCompressed && options.decompress) {
  1065. transformStreams.push(zlib.createGunzip());
  1066. }
  1067. pipeline(rawResponseStream, ...transformStreams, throughStream, onComplete);
  1068. };
  1069. // Authenticate the request, then pipe the remote API request to the stream
  1070. // returned to the user.
  1071. const makeRequest = () => {
  1072. const query = { alt: 'media' };
  1073. if (this.generation) {
  1074. query.generation = this.generation;
  1075. }
  1076. if (options.userProject) {
  1077. query.userProject = options.userProject;
  1078. }
  1079. const headers = {
  1080. 'Accept-Encoding': 'gzip',
  1081. 'Cache-Control': 'no-store',
  1082. };
  1083. if (rangeRequest) {
  1084. const start = typeof options.start === 'number' ? options.start : '0';
  1085. const end = typeof options.end === 'number' ? options.end : '';
  1086. headers.Range = `bytes=${tailRequest ? end : `${start}-${end}`}`;
  1087. }
  1088. const reqOpts = {
  1089. uri: '',
  1090. headers,
  1091. qs: query,
  1092. };
  1093. if (options[GCCL_GCS_CMD_KEY]) {
  1094. reqOpts[GCCL_GCS_CMD_KEY] = options[GCCL_GCS_CMD_KEY];
  1095. }
  1096. this.requestStream(reqOpts)
  1097. .on('error', err => {
  1098. throughStream.destroy(err);
  1099. })
  1100. .on('response', res => {
  1101. throughStream.emit('response', res);
  1102. util.handleResp(null, res, null, onResponse);
  1103. })
  1104. .resume();
  1105. };
  1106. throughStream.on('reading', makeRequest);
  1107. return throughStream;
  1108. }
  1109. /**
  1110. * @callback CreateResumableUploadCallback
  1111. * @param {?Error} err Request error, if any.
  1112. * @param {string} uri The resumable upload's unique session URI.
  1113. */
  1114. /**
  1115. * @typedef {array} CreateResumableUploadResponse
  1116. * @property {string} 0 The resumable upload's unique session URI.
  1117. */
  1118. /**
  1119. * @typedef {object} CreateResumableUploadOptions
  1120. * @property {object} [metadata] Metadata to set on the file.
  1121. * @property {number} [offset] The starting byte of the upload stream for resuming an interrupted upload.
  1122. * @property {string} [origin] Origin header to set for the upload.
  1123. * @property {string} [predefinedAcl] Apply a predefined set of access
  1124. * controls to this object.
  1125. *
  1126. * Acceptable values are:
  1127. * - **`authenticatedRead`** - Object owner gets `OWNER` access, and
  1128. * `allAuthenticatedUsers` get `READER` access.
  1129. *
  1130. * - **`bucketOwnerFullControl`** - Object owner gets `OWNER` access, and
  1131. * project team owners get `OWNER` access.
  1132. *
  1133. * - **`bucketOwnerRead`** - Object owner gets `OWNER` access, and project
  1134. * team owners get `READER` access.
  1135. *
  1136. * - **`private`** - Object owner gets `OWNER` access.
  1137. *
  1138. * - **`projectPrivate`** - Object owner gets `OWNER` access, and project
  1139. * team members get access according to their roles.
  1140. *
  1141. * - **`publicRead`** - Object owner gets `OWNER` access, and `allUsers`
  1142. * get `READER` access.
  1143. * @property {boolean} [private] Make the uploaded file private. (Alias for
  1144. * `options.predefinedAcl = 'private'`)
  1145. * @property {boolean} [public] Make the uploaded file public. (Alias for
  1146. * `options.predefinedAcl = 'publicRead'`)
  1147. * @property {string} [userProject] The ID of the project which will be
  1148. * billed for the request.
  1149. * @property {string} [chunkSize] Create a separate request per chunk. This
  1150. * value is in bytes and should be a multiple of 256 KiB (2^18).
  1151. * {@link https://cloud.google.com/storage/docs/performing-resumable-uploads#chunked-upload| We recommend using at least 8 MiB for the chunk size.}
  1152. */
  1153. /**
  1154. * Create a unique resumable upload session URI. This is the first step when
  1155. * performing a resumable upload.
  1156. *
  1157. * See the {@link https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload| Resumable upload guide}
  1158. * for more on how the entire process works.
  1159. *
  1160. * <h4>Note</h4>
  1161. *
  1162. * If you are just looking to perform a resumable upload without worrying
  1163. * about any of the details, see {@link File#createWriteStream}. Resumable
  1164. * uploads are performed by default.
  1165. *
  1166. * See {@link https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload| Resumable upload guide}
  1167. *
  1168. * @param {CreateResumableUploadOptions} [options] Configuration options.
  1169. * @param {CreateResumableUploadCallback} [callback] Callback function.
  1170. * @returns {Promise<CreateResumableUploadResponse>}
  1171. *
  1172. * @example
  1173. * ```
  1174. * const {Storage} = require('@google-cloud/storage');
  1175. * const storage = new Storage();
  1176. * const myBucket = storage.bucket('my-bucket');
  1177. *
  1178. * const file = myBucket.file('my-file');
  1179. * file.createResumableUpload(function(err, uri) {
  1180. * if (!err) {
  1181. * // `uri` can be used to PUT data to.
  1182. * }
  1183. * });
  1184. *
  1185. * //-
  1186. * // If the callback is omitted, we'll return a Promise.
  1187. * //-
  1188. * file.createResumableUpload().then(function(data) {
  1189. * const uri = data[0];
  1190. * });
  1191. * ```
  1192. */
  1193. createResumableUpload(optionsOrCallback, callback) {
  1194. var _a, _b;
  1195. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  1196. callback =
  1197. typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
  1198. const retryOptions = this.storage.retryOptions;
  1199. if ((((_a = options === null || options === void 0 ? void 0 : options.preconditionOpts) === null || _a === void 0 ? void 0 : _a.ifGenerationMatch) === undefined &&
  1200. ((_b = this.instancePreconditionOpts) === null || _b === void 0 ? void 0 : _b.ifGenerationMatch) === undefined &&
  1201. this.storage.retryOptions.idempotencyStrategy ===
  1202. IdempotencyStrategy.RetryConditional) ||
  1203. this.storage.retryOptions.idempotencyStrategy ===
  1204. IdempotencyStrategy.RetryNever) {
  1205. retryOptions.autoRetry = false;
  1206. }
  1207. resumableUpload.createURI({
  1208. authClient: this.storage.authClient,
  1209. apiEndpoint: this.storage.apiEndpoint,
  1210. bucket: this.bucket.name,
  1211. customRequestOptions: this.getRequestInterceptors().reduce((reqOpts, interceptorFn) => interceptorFn(reqOpts), {}),
  1212. file: this.name,
  1213. generation: this.generation,
  1214. key: this.encryptionKey,
  1215. kmsKeyName: this.kmsKeyName,
  1216. metadata: options.metadata,
  1217. offset: options.offset,
  1218. origin: options.origin,
  1219. predefinedAcl: options.predefinedAcl,
  1220. private: options.private,
  1221. public: options.public,
  1222. userProject: options.userProject || this.userProject,
  1223. retryOptions: retryOptions,
  1224. params: (options === null || options === void 0 ? void 0 : options.preconditionOpts) || this.instancePreconditionOpts,
  1225. universeDomain: this.bucket.storage.universeDomain,
  1226. [GCCL_GCS_CMD_KEY]: options[GCCL_GCS_CMD_KEY],
  1227. }, callback);
  1228. this.storage.retryOptions.autoRetry = this.instanceRetryValue;
  1229. }
  1230. /**
  1231. * @typedef {object} CreateWriteStreamOptions Configuration options for File#createWriteStream().
  1232. * @property {string} [contentType] Alias for
  1233. * `options.metadata.contentType`. If set to `auto`, the file name is used
  1234. * to determine the contentType.
  1235. * @property {string|boolean} [gzip] If true, automatically gzip the file.
  1236. * If set to `auto`, the contentType is used to determine if the file
  1237. * should be gzipped. This will set `options.metadata.contentEncoding` to
  1238. * `gzip` if necessary.
  1239. * @property {object} [metadata] See the examples below or
  1240. * {@link https://cloud.google.com/storage/docs/json_api/v1/objects/insert#request_properties_JSON| Objects: insert request body}
  1241. * for more details.
  1242. * @property {number} [offset] The starting byte of the upload stream, for
  1243. * resuming an interrupted upload. Defaults to 0.
  1244. * @property {string} [predefinedAcl] Apply a predefined set of access
  1245. * controls to this object.
  1246. *
  1247. * Acceptable values are:
  1248. * - **`authenticatedRead`** - Object owner gets `OWNER` access, and
  1249. * `allAuthenticatedUsers` get `READER` access.
  1250. *
  1251. * - **`bucketOwnerFullControl`** - Object owner gets `OWNER` access, and
  1252. * project team owners get `OWNER` access.
  1253. *
  1254. * - **`bucketOwnerRead`** - Object owner gets `OWNER` access, and project
  1255. * team owners get `READER` access.
  1256. *
  1257. * - **`private`** - Object owner gets `OWNER` access.
  1258. *
  1259. * - **`projectPrivate`** - Object owner gets `OWNER` access, and project
  1260. * team members get access according to their roles.
  1261. *
  1262. * - **`publicRead`** - Object owner gets `OWNER` access, and `allUsers`
  1263. * get `READER` access.
  1264. * @property {boolean} [private] Make the uploaded file private. (Alias for
  1265. * `options.predefinedAcl = 'private'`)
  1266. * @property {boolean} [public] Make the uploaded file public. (Alias for
  1267. * `options.predefinedAcl = 'publicRead'`)
  1268. * @property {boolean} [resumable] Force a resumable upload. NOTE: When
  1269. * working with streams, the file format and size is unknown until it's
  1270. * completely consumed. Because of this, it's best for you to be explicit
  1271. * for what makes sense given your input.
  1272. * @property {number} [timeout=60000] Set the HTTP request timeout in
  1273. * milliseconds. This option is not available for resumable uploads.
  1274. * Default: `60000`
  1275. * @property {string} [uri] The URI for an already-created resumable
  1276. * upload. See {@link File#createResumableUpload}.
  1277. * @property {string} [userProject] The ID of the project which will be
  1278. * billed for the request.
  1279. * @property {string|boolean} [validation] Possible values: `"md5"`,
  1280. * `"crc32c"`, or `false`. By default, data integrity is validated with a
  1281. * CRC32c checksum. You may use MD5 if preferred, but that hash is not
  1282. * supported for composite objects. An error will be raised if MD5 is
  1283. * specified but is not available. You may also choose to skip validation
  1284. * completely, however this is **not recommended**. In addition to specifying
  1285. * validation type, providing `metadata.crc32c` or `metadata.md5Hash` will
  1286. * cause the server to perform validation in addition to client validation.
  1287. * NOTE: Validation is automatically skipped for objects that were
  1288. * uploaded using the `gzip` option and have already compressed content.
  1289. */
  1290. /**
  1291. * Create a writable stream to overwrite the contents of the file in your
  1292. * bucket.
  1293. *
  1294. * A File object can also be used to create files for the first time.
  1295. *
  1296. * Resumable uploads are automatically enabled and must be shut off explicitly
  1297. * by setting `options.resumable` to `false`.
  1298. *
  1299. *
  1300. * <p class="notice">
  1301. * There is some overhead when using a resumable upload that can cause
  1302. * noticeable performance degradation while uploading a series of small
  1303. * files. When uploading files less than 10MB, it is recommended that the
  1304. * resumable feature is disabled.
  1305. * </p>
  1306. *
  1307. * NOTE: Writable streams will emit the `finish` event when the file is fully
  1308. * uploaded.
  1309. *
  1310. * See {@link https://cloud.google.com/storage/docs/json_api/v1/how-tos/upload Upload Options (Simple or Resumable)}
  1311. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objects/insert Objects: insert API Documentation}
  1312. *
  1313. * @param {CreateWriteStreamOptions} [options] Configuration options.
  1314. * @returns {WritableStream}
  1315. *
  1316. * @example
  1317. * ```
  1318. * const fs = require('fs');
  1319. * const {Storage} = require('@google-cloud/storage');
  1320. * const storage = new Storage();
  1321. * const myBucket = storage.bucket('my-bucket');
  1322. *
  1323. * const file = myBucket.file('my-file');
  1324. *
  1325. * //-
  1326. * // <h4>Uploading a File</h4>
  1327. * //
  1328. * // Now, consider a case where we want to upload a file to your bucket. You
  1329. * // have the option of using {@link Bucket#upload}, but that is just
  1330. * // a convenience method which will do the following.
  1331. * //-
  1332. * fs.createReadStream('/Users/stephen/Photos/birthday-at-the-zoo/panda.jpg')
  1333. * .pipe(file.createWriteStream())
  1334. * .on('error', function(err) {})
  1335. * .on('finish', function() {
  1336. * // The file upload is complete.
  1337. * });
  1338. *
  1339. * //-
  1340. * // <h4>Uploading a File with gzip compression</h4>
  1341. * //-
  1342. * fs.createReadStream('/Users/stephen/site/index.html')
  1343. * .pipe(file.createWriteStream({ gzip: true }))
  1344. * .on('error', function(err) {})
  1345. * .on('finish', function() {
  1346. * // The file upload is complete.
  1347. * });
  1348. *
  1349. * //-
  1350. * // Downloading the file with `createReadStream` will automatically decode
  1351. * // the file.
  1352. * //-
  1353. *
  1354. * //-
  1355. * // <h4>Uploading a File with Metadata</h4>
  1356. * //
  1357. * // One last case you may run into is when you want to upload a file to your
  1358. * // bucket and set its metadata at the same time. Like above, you can use
  1359. * // {@link Bucket#upload} to do this, which is just a wrapper around
  1360. * // the following.
  1361. * //-
  1362. * fs.createReadStream('/Users/stephen/Photos/birthday-at-the-zoo/panda.jpg')
  1363. * .pipe(file.createWriteStream({
  1364. * metadata: {
  1365. * contentType: 'image/jpeg',
  1366. * metadata: {
  1367. * custom: 'metadata'
  1368. * }
  1369. * }
  1370. * }))
  1371. * .on('error', function(err) {})
  1372. * .on('finish', function() {
  1373. * // The file upload is complete.
  1374. * });
  1375. * ```
  1376. *
  1377. * //-
  1378. * // <h4>Continuing a Resumable Upload</h4>
  1379. * //
  1380. * // One can capture a `uri` from a resumable upload to reuse later.
  1381. * // Additionally, for validation, one can also capture and pass `crc32c`.
  1382. * //-
  1383. * let uri: string | undefined = undefined;
  1384. * let resumeCRC32C: string | undefined = undefined;
  1385. *
  1386. * fs.createWriteStream()
  1387. * .on('uri', link => {uri = link})
  1388. * .on('crc32', crc32c => {resumeCRC32C = crc32c});
  1389. *
  1390. * // later...
  1391. * fs.createWriteStream({uri, resumeCRC32C});
  1392. */
  1393. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  1394. createWriteStream(options = {}) {
  1395. var _a;
  1396. (_a = options.metadata) !== null && _a !== void 0 ? _a : (options.metadata = {});
  1397. if (options.contentType) {
  1398. options.metadata.contentType = options.contentType;
  1399. }
  1400. if (!options.metadata.contentType ||
  1401. options.metadata.contentType === 'auto') {
  1402. const detectedContentType = mime.getType(this.name);
  1403. if (detectedContentType) {
  1404. options.metadata.contentType = detectedContentType;
  1405. }
  1406. }
  1407. let gzip = options.gzip;
  1408. if (gzip === 'auto') {
  1409. gzip = COMPRESSIBLE_MIME_REGEX.test(options.metadata.contentType || '');
  1410. }
  1411. if (gzip) {
  1412. options.metadata.contentEncoding = 'gzip';
  1413. }
  1414. let crc32c = true;
  1415. let md5 = false;
  1416. if (typeof options.validation === 'string') {
  1417. options.validation = options.validation.toLowerCase();
  1418. crc32c = options.validation === 'crc32c';
  1419. md5 = options.validation === 'md5';
  1420. }
  1421. else if (options.validation === false) {
  1422. crc32c = false;
  1423. md5 = false;
  1424. }
  1425. if (options.offset) {
  1426. if (md5) {
  1427. throw new RangeError(FileExceptionMessages.MD5_RESUMED_UPLOAD);
  1428. }
  1429. if (crc32c && !options.isPartialUpload && !options.resumeCRC32C) {
  1430. throw new RangeError(FileExceptionMessages.MISSING_RESUME_CRC32C_FINAL_UPLOAD);
  1431. }
  1432. }
  1433. /**
  1434. * A callback for determining when the underlying pipeline is complete.
  1435. * It's possible the pipeline callback could error before the write stream
  1436. * calls `final` so by default this will destroy the write stream unless the
  1437. * write stream sets this callback via its `final` handler.
  1438. * @param error An optional error
  1439. */
  1440. let pipelineCallback = error => {
  1441. writeStream.destroy(error || undefined);
  1442. };
  1443. // A stream for consumer to write to
  1444. const writeStream = new Writable({
  1445. final(cb) {
  1446. // Set the pipeline callback to this callback so the pipeline's results
  1447. // can be populated to the consumer
  1448. pipelineCallback = cb;
  1449. emitStream.end();
  1450. },
  1451. write(chunk, encoding, cb) {
  1452. emitStream.write(chunk, encoding, cb);
  1453. },
  1454. });
  1455. // If the write stream, which is returned to the caller, catches an error we need to make sure that
  1456. // at least one of the streams in the pipeline below gets notified so that they
  1457. // all get cleaned up / destroyed.
  1458. writeStream.once('error', e => {
  1459. emitStream.destroy(e);
  1460. });
  1461. // If the write stream is closed, cleanup the pipeline below by calling destroy on one of the streams.
  1462. writeStream.once('close', () => {
  1463. emitStream.destroy();
  1464. });
  1465. const transformStreams = [];
  1466. if (gzip) {
  1467. transformStreams.push(zlib.createGzip());
  1468. }
  1469. const emitStream = new PassThroughShim();
  1470. let hashCalculatingStream = null;
  1471. if (crc32c || md5) {
  1472. const crc32cInstance = options.resumeCRC32C
  1473. ? CRC32C.from(options.resumeCRC32C)
  1474. : undefined;
  1475. hashCalculatingStream = new HashStreamValidator({
  1476. crc32c,
  1477. crc32cInstance,
  1478. md5,
  1479. crc32cGenerator: this.crc32cGenerator,
  1480. updateHashesOnly: true,
  1481. });
  1482. transformStreams.push(hashCalculatingStream);
  1483. }
  1484. const fileWriteStream = duplexify();
  1485. let fileWriteStreamMetadataReceived = false;
  1486. // Handing off emitted events to users
  1487. emitStream.on('reading', () => writeStream.emit('reading'));
  1488. emitStream.on('writing', () => writeStream.emit('writing'));
  1489. fileWriteStream.on('uri', evt => writeStream.emit('uri', evt));
  1490. fileWriteStream.on('progress', evt => writeStream.emit('progress', evt));
  1491. fileWriteStream.on('response', resp => writeStream.emit('response', resp));
  1492. fileWriteStream.once('metadata', () => {
  1493. fileWriteStreamMetadataReceived = true;
  1494. });
  1495. writeStream.once('writing', () => {
  1496. if (options.resumable === false) {
  1497. this.startSimpleUpload_(fileWriteStream, options);
  1498. }
  1499. else {
  1500. this.startResumableUpload_(fileWriteStream, options);
  1501. }
  1502. pipeline(emitStream, ...transformStreams, fileWriteStream, async (e) => {
  1503. if (e) {
  1504. return pipelineCallback(e);
  1505. }
  1506. // We want to make sure we've received the metadata from the server in order
  1507. // to properly validate the object's integrity. Depending on the type of upload,
  1508. // the stream could close before the response is returned.
  1509. if (!fileWriteStreamMetadataReceived) {
  1510. try {
  1511. await new Promise((resolve, reject) => {
  1512. fileWriteStream.once('metadata', resolve);
  1513. fileWriteStream.once('error', reject);
  1514. });
  1515. }
  1516. catch (e) {
  1517. return pipelineCallback(e);
  1518. }
  1519. }
  1520. // Emit the local CRC32C value for future validation, if validation is enabled.
  1521. if (hashCalculatingStream === null || hashCalculatingStream === void 0 ? void 0 : hashCalculatingStream.crc32c) {
  1522. writeStream.emit('crc32c', hashCalculatingStream.crc32c);
  1523. }
  1524. try {
  1525. // Metadata may not be ready if the upload is a partial upload,
  1526. // nothing to validate yet.
  1527. const metadataNotReady = options.isPartialUpload && !this.metadata;
  1528. if (hashCalculatingStream && !metadataNotReady) {
  1529. await __classPrivateFieldGet(this, _File_instances, "m", _File_validateIntegrity).call(this, hashCalculatingStream, {
  1530. crc32c,
  1531. md5,
  1532. });
  1533. }
  1534. pipelineCallback();
  1535. }
  1536. catch (e) {
  1537. pipelineCallback(e);
  1538. }
  1539. });
  1540. });
  1541. return writeStream;
  1542. }
  1543. delete(optionsOrCallback, cb) {
  1544. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  1545. cb = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
  1546. this.disableAutoRetryConditionallyIdempotent_(this.methods.delete, AvailableServiceObjectMethods.delete, options);
  1547. super
  1548. .delete(options)
  1549. .then(resp => cb(null, ...resp))
  1550. .catch(cb)
  1551. .finally(() => {
  1552. this.storage.retryOptions.autoRetry = this.instanceRetryValue;
  1553. });
  1554. }
  1555. /**
  1556. * @typedef {array} DownloadResponse
  1557. * @property [0] The contents of a File.
  1558. */
  1559. /**
  1560. * @callback DownloadCallback
  1561. * @param err Request error, if any.
  1562. * @param contents The contents of a File.
  1563. */
  1564. /**
  1565. * Convenience method to download a file into memory or to a local
  1566. * destination.
  1567. *
  1568. * @param {object} [options] Configuration options. The arguments match those
  1569. * passed to {@link File#createReadStream}.
  1570. * @param {string} [options.destination] Local file path to write the file's
  1571. * contents to.
  1572. * @param {string} [options.userProject] The ID of the project which will be
  1573. * billed for the request.
  1574. * @param {DownloadCallback} [callback] Callback function.
  1575. * @returns {Promise<DownloadResponse>}
  1576. *
  1577. * @example
  1578. * ```
  1579. * const {Storage} = require('@google-cloud/storage');
  1580. * const storage = new Storage();
  1581. * const myBucket = storage.bucket('my-bucket');
  1582. *
  1583. * const file = myBucket.file('my-file');
  1584. *
  1585. * //-
  1586. * // Download a file into memory. The contents will be available as the
  1587. * second
  1588. * // argument in the demonstration below, `contents`.
  1589. * //-
  1590. * file.download(function(err, contents) {});
  1591. *
  1592. * //-
  1593. * // Download a file to a local destination.
  1594. * //-
  1595. * file.download({
  1596. * destination: '/Users/me/Desktop/file-backup.txt'
  1597. * }, function(err) {});
  1598. *
  1599. * //-
  1600. * // If the callback is omitted, we'll return a Promise.
  1601. * //-
  1602. * file.download().then(function(data) {
  1603. * const contents = data[0];
  1604. * });
  1605. *
  1606. * ```
  1607. * @example <caption>include:samples/files.js</caption>
  1608. * region_tag:storage_download_file
  1609. * Another example:
  1610. *
  1611. * @example <caption>include:samples/encryption.js</caption>
  1612. * region_tag:storage_download_encrypted_file
  1613. * Example of downloading an encrypted file:
  1614. *
  1615. * @example <caption>include:samples/requesterPays.js</caption>
  1616. * region_tag:storage_download_file_requester_pays
  1617. * Example of downloading a file where the requester pays:
  1618. */
  1619. download(optionsOrCallback, cb) {
  1620. let options;
  1621. if (typeof optionsOrCallback === 'function') {
  1622. cb = optionsOrCallback;
  1623. options = {};
  1624. }
  1625. else {
  1626. options = optionsOrCallback;
  1627. }
  1628. let called = false;
  1629. const callback = ((...args) => {
  1630. if (!called)
  1631. cb(...args);
  1632. called = true;
  1633. });
  1634. const destination = options.destination;
  1635. delete options.destination;
  1636. const fileStream = this.createReadStream(options);
  1637. let receivedData = false;
  1638. if (destination) {
  1639. fileStream
  1640. .on('error', callback)
  1641. .once('data', data => {
  1642. receivedData = true;
  1643. // We know that the file exists the server - now we can truncate/write to a file
  1644. const writable = fs.createWriteStream(destination);
  1645. writable.write(data);
  1646. fileStream
  1647. .pipe(writable)
  1648. .on('error', callback)
  1649. .on('finish', callback);
  1650. })
  1651. .on('end', () => {
  1652. // In the case of an empty file no data will be received before the end event fires
  1653. if (!receivedData) {
  1654. const data = Buffer.alloc(0);
  1655. try {
  1656. fs.writeFileSync(destination, data);
  1657. callback(null, data);
  1658. }
  1659. catch (e) {
  1660. callback(e, data);
  1661. }
  1662. }
  1663. });
  1664. }
  1665. else {
  1666. this.getBufferFromReadable(fileStream)
  1667. .then(contents => callback === null || callback === void 0 ? void 0 : callback(null, contents))
  1668. .catch(callback);
  1669. }
  1670. }
  1671. /**
  1672. * The Storage API allows you to use a custom key for server-side encryption.
  1673. *
  1674. * See {@link https://cloud.google.com/storage/docs/encryption#customer-supplied| Customer-supplied Encryption Keys}
  1675. *
  1676. * @param {string|buffer} encryptionKey An AES-256 encryption key.
  1677. * @returns {File}
  1678. *
  1679. * @example
  1680. * ```
  1681. * const crypto = require('crypto');
  1682. * const {Storage} = require('@google-cloud/storage');
  1683. * const storage = new Storage();
  1684. * const myBucket = storage.bucket('my-bucket');
  1685. *
  1686. * const encryptionKey = crypto.randomBytes(32);
  1687. *
  1688. * const fileWithCustomEncryption = myBucket.file('my-file');
  1689. * fileWithCustomEncryption.setEncryptionKey(encryptionKey);
  1690. *
  1691. * const fileWithoutCustomEncryption = myBucket.file('my-file');
  1692. *
  1693. * fileWithCustomEncryption.save('data', function(err) {
  1694. * // Try to download with the File object that hasn't had
  1695. * // `setEncryptionKey()` called:
  1696. * fileWithoutCustomEncryption.download(function(err) {
  1697. * // We will receive an error:
  1698. * // err.message === 'Bad Request'
  1699. *
  1700. * // Try again with the File object we called `setEncryptionKey()` on:
  1701. * fileWithCustomEncryption.download(function(err, contents) {
  1702. * // contents.toString() === 'data'
  1703. * });
  1704. * });
  1705. * });
  1706. *
  1707. * ```
  1708. * @example <caption>include:samples/encryption.js</caption>
  1709. * region_tag:storage_upload_encrypted_file
  1710. * Example of uploading an encrypted file:
  1711. *
  1712. * @example <caption>include:samples/encryption.js</caption>
  1713. * region_tag:storage_download_encrypted_file
  1714. * Example of downloading an encrypted file:
  1715. */
  1716. setEncryptionKey(encryptionKey) {
  1717. this.encryptionKey = encryptionKey;
  1718. this.encryptionKeyBase64 = Buffer.from(encryptionKey).toString('base64');
  1719. this.encryptionKeyHash = crypto
  1720. .createHash('sha256')
  1721. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  1722. .update(this.encryptionKeyBase64, 'base64')
  1723. .digest('base64');
  1724. this.encryptionKeyInterceptor = {
  1725. request: reqOpts => {
  1726. reqOpts.headers = reqOpts.headers || {};
  1727. reqOpts.headers['x-goog-encryption-algorithm'] = 'AES256';
  1728. reqOpts.headers['x-goog-encryption-key'] = this.encryptionKeyBase64;
  1729. reqOpts.headers['x-goog-encryption-key-sha256'] =
  1730. this.encryptionKeyHash;
  1731. return reqOpts;
  1732. },
  1733. };
  1734. this.interceptors.push(this.encryptionKeyInterceptor);
  1735. return this;
  1736. }
  1737. /**
  1738. * Gets a reference to a Cloud Storage {@link File} file from the provided URL in string format.
  1739. * @param {string} publicUrlOrGsUrl the URL as a string. Must be of the format gs://bucket/file
  1740. * or https://storage.googleapis.com/bucket/file.
  1741. * @param {Storage} storageInstance an instance of a Storage object.
  1742. * @param {FileOptions} [options] Configuration options
  1743. * @returns {File}
  1744. */
  1745. static from(publicUrlOrGsUrl, storageInstance, options) {
  1746. const gsMatches = [...publicUrlOrGsUrl.matchAll(GS_UTIL_URL_REGEX)];
  1747. const httpsMatches = [...publicUrlOrGsUrl.matchAll(HTTPS_PUBLIC_URL_REGEX)];
  1748. if (gsMatches.length > 0) {
  1749. const bucket = new Bucket(storageInstance, gsMatches[0][2]);
  1750. return new File(bucket, gsMatches[0][3], options);
  1751. }
  1752. else if (httpsMatches.length > 0) {
  1753. const bucket = new Bucket(storageInstance, httpsMatches[0][3]);
  1754. return new File(bucket, httpsMatches[0][4], options);
  1755. }
  1756. else {
  1757. throw new Error('URL string must be of format gs://bucket/file or https://storage.googleapis.com/bucket/file');
  1758. }
  1759. }
  1760. get(optionsOrCallback, cb) {
  1761. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  1762. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  1763. cb =
  1764. typeof optionsOrCallback === 'function'
  1765. ? optionsOrCallback
  1766. : cb;
  1767. super
  1768. .get(options)
  1769. .then(resp => cb(null, ...resp))
  1770. .catch(cb);
  1771. }
  1772. /**
  1773. * @typedef {array} GetExpirationDateResponse
  1774. * @property {date} 0 A Date object representing the earliest time this file's
  1775. * retention policy will expire.
  1776. */
  1777. /**
  1778. * @callback GetExpirationDateCallback
  1779. * @param {?Error} err Request error, if any.
  1780. * @param {date} expirationDate A Date object representing the earliest time
  1781. * this file's retention policy will expire.
  1782. */
  1783. /**
  1784. * If this bucket has a retention policy defined, use this method to get a
  1785. * Date object representing the earliest time this file will expire.
  1786. *
  1787. * @param {GetExpirationDateCallback} [callback] Callback function.
  1788. * @returns {Promise<GetExpirationDateResponse>}
  1789. *
  1790. * @example
  1791. * ```
  1792. * const storage = require('@google-cloud/storage')();
  1793. * const myBucket = storage.bucket('my-bucket');
  1794. *
  1795. * const file = myBucket.file('my-file');
  1796. *
  1797. * file.getExpirationDate(function(err, expirationDate) {
  1798. * // expirationDate is a Date object.
  1799. * });
  1800. * ```
  1801. */
  1802. getExpirationDate(callback) {
  1803. this.getMetadata((err, metadata, apiResponse) => {
  1804. if (err) {
  1805. callback(err, null, apiResponse);
  1806. return;
  1807. }
  1808. if (!metadata.retentionExpirationTime) {
  1809. const error = new Error(FileExceptionMessages.EXPIRATION_TIME_NA);
  1810. callback(error, null, apiResponse);
  1811. return;
  1812. }
  1813. callback(null, new Date(metadata.retentionExpirationTime), apiResponse);
  1814. });
  1815. }
  1816. /**
  1817. * @typedef {array} GenerateSignedPostPolicyV2Response
  1818. * @property {object} 0 The document policy.
  1819. */
  1820. /**
  1821. * @callback GenerateSignedPostPolicyV2Callback
  1822. * @param {?Error} err Request error, if any.
  1823. * @param {object} policy The document policy.
  1824. */
  1825. /**
  1826. * Get a signed policy document to allow a user to upload data with a POST
  1827. * request.
  1828. *
  1829. * In Google Cloud Platform environments, such as Cloud Functions and App
  1830. * Engine, you usually don't provide a `keyFilename` or `credentials` during
  1831. * instantiation. In those environments, we call the
  1832. * {@link https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob| signBlob API}
  1833. * to create a signed policy. That API requires either the
  1834. * `https://www.googleapis.com/auth/iam` or
  1835. * `https://www.googleapis.com/auth/cloud-platform` scope, so be sure they are
  1836. * enabled.
  1837. *
  1838. * See {@link https://cloud.google.com/storage/docs/xml-api/post-object-v2| POST Object with the V2 signing process}
  1839. *
  1840. * @throws {Error} If an expiration timestamp from the past is given.
  1841. * @throws {Error} If options.equals has an array with less or more than two
  1842. * members.
  1843. * @throws {Error} If options.startsWith has an array with less or more than two
  1844. * members.
  1845. *
  1846. * @param {object} options Configuration options.
  1847. * @param {array|array[]} [options.equals] Array of request parameters and
  1848. * their expected value (e.g. [['$<field>', '<value>']]). Values are
  1849. * translated into equality constraints in the conditions field of the
  1850. * policy document (e.g. ['eq', '$<field>', '<value>']). If only one
  1851. * equality condition is to be specified, options.equals can be a one-
  1852. * dimensional array (e.g. ['$<field>', '<value>']).
  1853. * @param {*} options.expires - A timestamp when this policy will expire. Any
  1854. * value given is passed to `new Date()`.
  1855. * @param {array|array[]} [options.startsWith] Array of request parameters and
  1856. * their expected prefixes (e.g. [['$<field>', '<value>']). Values are
  1857. * translated into starts-with constraints in the conditions field of the
  1858. * policy document (e.g. ['starts-with', '$<field>', '<value>']). If only
  1859. * one prefix condition is to be specified, options.startsWith can be a
  1860. * one- dimensional array (e.g. ['$<field>', '<value>']).
  1861. * @param {string} [options.acl] ACL for the object from possibly predefined
  1862. * ACLs.
  1863. * @param {string} [options.successRedirect] The URL to which the user client
  1864. * is redirected if the upload is successful.
  1865. * @param {string} [options.successStatus] - The status of the Google Storage
  1866. * response if the upload is successful (must be string).
  1867. * @param {object} [options.contentLengthRange]
  1868. * @param {number} [options.contentLengthRange.min] Minimum value for the
  1869. * request's content length.
  1870. * @param {number} [options.contentLengthRange.max] Maximum value for the
  1871. * request's content length.
  1872. * @param {GenerateSignedPostPolicyV2Callback} [callback] Callback function.
  1873. * @returns {Promise<GenerateSignedPostPolicyV2Response>}
  1874. *
  1875. * @example
  1876. * ```
  1877. * const {Storage} = require('@google-cloud/storage');
  1878. * const storage = new Storage();
  1879. * const myBucket = storage.bucket('my-bucket');
  1880. *
  1881. * const file = myBucket.file('my-file');
  1882. * const options = {
  1883. * equals: ['$Content-Type', 'image/jpeg'],
  1884. * expires: '10-25-2022',
  1885. * contentLengthRange: {
  1886. * min: 0,
  1887. * max: 1024
  1888. * }
  1889. * };
  1890. *
  1891. * file.generateSignedPostPolicyV2(options, function(err, policy) {
  1892. * // policy.string: the policy document in plain text.
  1893. * // policy.base64: the policy document in base64.
  1894. * // policy.signature: the policy signature in base64.
  1895. * });
  1896. *
  1897. * //-
  1898. * // If the callback is omitted, we'll return a Promise.
  1899. * //-
  1900. * file.generateSignedPostPolicyV2(options).then(function(data) {
  1901. * const policy = data[0];
  1902. * });
  1903. * ```
  1904. */
  1905. generateSignedPostPolicyV2(optionsOrCallback, cb) {
  1906. const args = normalize(optionsOrCallback, cb);
  1907. let options = args.options;
  1908. const callback = args.callback;
  1909. const expires = new Date(options.expires);
  1910. if (isNaN(expires.getTime())) {
  1911. throw new Error(ExceptionMessages.EXPIRATION_DATE_INVALID);
  1912. }
  1913. if (expires.valueOf() < Date.now()) {
  1914. throw new Error(ExceptionMessages.EXPIRATION_DATE_PAST);
  1915. }
  1916. options = Object.assign({}, options);
  1917. const conditions = [
  1918. ['eq', '$key', this.name],
  1919. {
  1920. bucket: this.bucket.name,
  1921. },
  1922. ];
  1923. if (Array.isArray(options.equals)) {
  1924. if (!Array.isArray(options.equals[0])) {
  1925. options.equals = [options.equals];
  1926. }
  1927. options.equals.forEach(condition => {
  1928. if (!Array.isArray(condition) || condition.length !== 2) {
  1929. throw new Error(FileExceptionMessages.EQUALS_CONDITION_TWO_ELEMENTS);
  1930. }
  1931. conditions.push(['eq', condition[0], condition[1]]);
  1932. });
  1933. }
  1934. if (Array.isArray(options.startsWith)) {
  1935. if (!Array.isArray(options.startsWith[0])) {
  1936. options.startsWith = [options.startsWith];
  1937. }
  1938. options.startsWith.forEach(condition => {
  1939. if (!Array.isArray(condition) || condition.length !== 2) {
  1940. throw new Error(FileExceptionMessages.STARTS_WITH_TWO_ELEMENTS);
  1941. }
  1942. conditions.push(['starts-with', condition[0], condition[1]]);
  1943. });
  1944. }
  1945. if (options.acl) {
  1946. conditions.push({
  1947. acl: options.acl,
  1948. });
  1949. }
  1950. if (options.successRedirect) {
  1951. conditions.push({
  1952. success_action_redirect: options.successRedirect,
  1953. });
  1954. }
  1955. if (options.successStatus) {
  1956. conditions.push({
  1957. success_action_status: options.successStatus,
  1958. });
  1959. }
  1960. if (options.contentLengthRange) {
  1961. const min = options.contentLengthRange.min;
  1962. const max = options.contentLengthRange.max;
  1963. if (typeof min !== 'number' || typeof max !== 'number') {
  1964. throw new Error(FileExceptionMessages.CONTENT_LENGTH_RANGE_MIN_MAX);
  1965. }
  1966. conditions.push(['content-length-range', min, max]);
  1967. }
  1968. const policy = {
  1969. expiration: expires.toISOString(),
  1970. conditions,
  1971. };
  1972. const policyString = JSON.stringify(policy);
  1973. const policyBase64 = Buffer.from(policyString).toString('base64');
  1974. this.storage.authClient.sign(policyBase64, options.signingEndpoint).then(signature => {
  1975. callback(null, {
  1976. string: policyString,
  1977. base64: policyBase64,
  1978. signature,
  1979. });
  1980. }, err => {
  1981. callback(new SigningError(err.message));
  1982. });
  1983. }
  1984. /**
  1985. * @typedef {object} SignedPostPolicyV4Output
  1986. * @property {string} url The request URL.
  1987. * @property {object} fields The form fields to include in the POST request.
  1988. */
  1989. /**
  1990. * @typedef {array} GenerateSignedPostPolicyV4Response
  1991. * @property {SignedPostPolicyV4Output} 0 An object containing the request URL and form fields.
  1992. */
  1993. /**
  1994. * @callback GenerateSignedPostPolicyV4Callback
  1995. * @param {?Error} err Request error, if any.
  1996. * @param {SignedPostPolicyV4Output} output An object containing the request URL and form fields.
  1997. */
  1998. /**
  1999. * Get a v4 signed policy document to allow a user to upload data with a POST
  2000. * request.
  2001. *
  2002. * In Google Cloud Platform environments, such as Cloud Functions and App
  2003. * Engine, you usually don't provide a `keyFilename` or `credentials` during
  2004. * instantiation. In those environments, we call the
  2005. * {@link https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob| signBlob API}
  2006. * to create a signed policy. That API requires either the
  2007. * `https://www.googleapis.com/auth/iam` or
  2008. * `https://www.googleapis.com/auth/cloud-platform` scope, so be sure they are
  2009. * enabled.
  2010. *
  2011. * See {@link https://cloud.google.com/storage/docs/xml-api/post-object#policydocument| Policy Document Reference}
  2012. *
  2013. * @param {object} options Configuration options.
  2014. * @param {Date|number|string} options.expires - A timestamp when this policy will expire. Any
  2015. * value given is passed to `new Date()`.
  2016. * @param {boolean} [config.virtualHostedStyle=false] Use virtual hosted-style
  2017. * URLs ('https://mybucket.storage.googleapis.com/...') instead of path-style
  2018. * ('https://storage.googleapis.com/mybucket/...'). Virtual hosted-style URLs
  2019. * should generally be preferred instead of path-style URL.
  2020. * Currently defaults to `false` for path-style, although this may change in a
  2021. * future major-version release.
  2022. * @param {string} [config.bucketBoundHostname] The bucket-bound hostname to return in
  2023. * the result, e.g. "https://cdn.example.com".
  2024. * @param {object} [config.fields] [Form fields]{@link https://cloud.google.com/storage/docs/xml-api/post-object#policydocument}
  2025. * to include in the signed policy. Any fields with key beginning with 'x-ignore-'
  2026. * will not be included in the policy to be signed.
  2027. * @param {object[]} [config.conditions] [Conditions]{@link https://cloud.google.com/storage/docs/authentication/signatures#policy-document}
  2028. * to include in the signed policy. All fields given in `config.fields` are
  2029. * automatically included in the conditions array, adding the same entry
  2030. * in both `fields` and `conditions` will result in duplicate entries.
  2031. *
  2032. * @param {GenerateSignedPostPolicyV4Callback} [callback] Callback function.
  2033. * @returns {Promise<GenerateSignedPostPolicyV4Response>}
  2034. *
  2035. * @example
  2036. * ```
  2037. * const {Storage} = require('@google-cloud/storage');
  2038. * const storage = new Storage();
  2039. * const myBucket = storage.bucket('my-bucket');
  2040. *
  2041. * const file = myBucket.file('my-file');
  2042. * const options = {
  2043. * expires: '10-25-2022',
  2044. * conditions: [
  2045. * ['eq', '$Content-Type', 'image/jpeg'],
  2046. * ['content-length-range', 0, 1024],
  2047. * ],
  2048. * fields: {
  2049. * acl: 'public-read',
  2050. * 'x-goog-meta-foo': 'bar',
  2051. * 'x-ignore-mykey': 'data'
  2052. * }
  2053. * };
  2054. *
  2055. * file.generateSignedPostPolicyV4(options, function(err, response) {
  2056. * // response.url The request URL
  2057. * // response.fields The form fields (including the signature) to include
  2058. * // to be used to upload objects by HTML forms.
  2059. * });
  2060. *
  2061. * //-
  2062. * // If the callback is omitted, we'll return a Promise.
  2063. * //-
  2064. * file.generateSignedPostPolicyV4(options).then(function(data) {
  2065. * const response = data[0];
  2066. * // response.url The request URL
  2067. * // response.fields The form fields (including the signature) to include
  2068. * // to be used to upload objects by HTML forms.
  2069. * });
  2070. * ```
  2071. */
  2072. generateSignedPostPolicyV4(optionsOrCallback, cb) {
  2073. const args = normalize(optionsOrCallback, cb);
  2074. let options = args.options;
  2075. const callback = args.callback;
  2076. const expires = new Date(options.expires);
  2077. if (isNaN(expires.getTime())) {
  2078. throw new Error(ExceptionMessages.EXPIRATION_DATE_INVALID);
  2079. }
  2080. if (expires.valueOf() < Date.now()) {
  2081. throw new Error(ExceptionMessages.EXPIRATION_DATE_PAST);
  2082. }
  2083. if (expires.valueOf() - Date.now() > SEVEN_DAYS * 1000) {
  2084. throw new Error(`Max allowed expiration is seven days (${SEVEN_DAYS} seconds).`);
  2085. }
  2086. options = Object.assign({}, options);
  2087. let fields = Object.assign({}, options.fields);
  2088. const now = new Date();
  2089. const nowISO = formatAsUTCISO(now, true);
  2090. const todayISO = formatAsUTCISO(now);
  2091. const sign = async () => {
  2092. const { client_email } = await this.storage.authClient.getCredentials();
  2093. const credential = `${client_email}/${todayISO}/auto/storage/goog4_request`;
  2094. fields = {
  2095. ...fields,
  2096. bucket: this.bucket.name,
  2097. key: this.name,
  2098. 'x-goog-date': nowISO,
  2099. 'x-goog-credential': credential,
  2100. 'x-goog-algorithm': 'GOOG4-RSA-SHA256',
  2101. };
  2102. const conditions = options.conditions || [];
  2103. Object.entries(fields).forEach(([key, value]) => {
  2104. if (!key.startsWith('x-ignore-')) {
  2105. conditions.push({ [key]: value });
  2106. }
  2107. });
  2108. delete fields.bucket;
  2109. const expiration = formatAsUTCISO(expires, true, '-', ':');
  2110. const policy = {
  2111. conditions,
  2112. expiration,
  2113. };
  2114. const policyString = unicodeJSONStringify(policy);
  2115. const policyBase64 = Buffer.from(policyString).toString('base64');
  2116. try {
  2117. const signature = await this.storage.authClient.sign(policyBase64, options.signingEndpoint);
  2118. const signatureHex = Buffer.from(signature, 'base64').toString('hex');
  2119. const universe = this.parent.storage.universeDomain;
  2120. fields['policy'] = policyBase64;
  2121. fields['x-goog-signature'] = signatureHex;
  2122. let url;
  2123. if (this.storage.customEndpoint) {
  2124. url = this.storage.apiEndpoint;
  2125. }
  2126. else if (options.virtualHostedStyle) {
  2127. url = `https://${this.bucket.name}.storage.${universe}/`;
  2128. }
  2129. else if (options.bucketBoundHostname) {
  2130. url = `${options.bucketBoundHostname}/`;
  2131. }
  2132. else {
  2133. url = `https://storage.${universe}/${this.bucket.name}/`;
  2134. }
  2135. return {
  2136. url,
  2137. fields,
  2138. };
  2139. }
  2140. catch (err) {
  2141. throw new SigningError(err.message);
  2142. }
  2143. };
  2144. sign().then(res => callback(null, res), callback);
  2145. }
  2146. /**
  2147. * @typedef {array} GetSignedUrlResponse
  2148. * @property {object} 0 The signed URL.
  2149. */
  2150. /**
  2151. * @callback GetSignedUrlCallback
  2152. * @param {?Error} err Request error, if any.
  2153. * @param {object} url The signed URL.
  2154. */
  2155. /**
  2156. * Get a signed URL to allow limited time access to the file.
  2157. *
  2158. * In Google Cloud Platform environments, such as Cloud Functions and App
  2159. * Engine, you usually don't provide a `keyFilename` or `credentials` during
  2160. * instantiation. In those environments, we call the
  2161. * {@link https://cloud.google.com/iam/docs/reference/credentials/rest/v1/projects.serviceAccounts/signBlob| signBlob API}
  2162. * to create a signed URL. That API requires either the
  2163. * `https://www.googleapis.com/auth/iam` or
  2164. * `https://www.googleapis.com/auth/cloud-platform` scope, so be sure they are
  2165. * enabled.
  2166. *
  2167. * See {@link https://cloud.google.com/storage/docs/access-control/signed-urls| Signed URLs Reference}
  2168. *
  2169. * @throws {Error} if an expiration timestamp from the past is given.
  2170. *
  2171. * @param {object} config Configuration object.
  2172. * @param {string} config.action "read" (HTTP: GET), "write" (HTTP: PUT), or
  2173. * "delete" (HTTP: DELETE), "resumable" (HTTP: POST).
  2174. * When using "resumable", the header `X-Goog-Resumable: start` has
  2175. * to be sent when making a request with the signed URL.
  2176. * @param {*} config.expires A timestamp when this link will expire. Any value
  2177. * given is passed to `new Date()`.
  2178. * Note: 'v4' supports maximum duration of 7 days (604800 seconds) from now.
  2179. * See [reference]{@link https://cloud.google.com/storage/docs/access-control/signed-urls#example}
  2180. * @param {string} [config.version='v2'] The signing version to use, either
  2181. * 'v2' or 'v4'.
  2182. * @param {boolean} [config.virtualHostedStyle=false] Use virtual hosted-style
  2183. * URLs (e.g. 'https://mybucket.storage.googleapis.com/...') instead of path-style
  2184. * (e.g. 'https://storage.googleapis.com/mybucket/...'). Virtual hosted-style URLs
  2185. * should generally be preferred instaed of path-style URL.
  2186. * Currently defaults to `false` for path-style, although this may change in a
  2187. * future major-version release.
  2188. * @param {string} [config.cname] The cname for this bucket, i.e.,
  2189. * "https://cdn.example.com".
  2190. * @param {string} [config.contentMd5] The MD5 digest value in base64. Just like
  2191. * if you provide this, the client must provide this HTTP header with this same
  2192. * value in its request, so to if this parameter is not provided here,
  2193. * the client must not provide any value for this HTTP header in its request.
  2194. * @param {string} [config.contentType] Just like if you provide this, the client
  2195. * must provide this HTTP header with this same value in its request, so to if
  2196. * this parameter is not provided here, the client must not provide any value
  2197. * for this HTTP header in its request.
  2198. * @param {object} [config.extensionHeaders] If these headers are used, the
  2199. * server will check to make sure that the client provides matching
  2200. * values. See {@link https://cloud.google.com/storage/docs/access-control/signed-urls#about-canonical-extension-headers| Canonical extension headers}
  2201. * for the requirements of this feature, most notably:
  2202. * - The header name must be prefixed with `x-goog-`
  2203. * - The header name must be all lowercase
  2204. *
  2205. * Note: Multi-valued header passed as an array in the extensionHeaders
  2206. * object is converted into a string, delimited by `,` with
  2207. * no space. Requests made using the signed URL will need to
  2208. * delimit multi-valued headers using a single `,` as well, or
  2209. * else the server will report a mismatched signature.
  2210. * @param {object} [config.queryParams] Additional query parameters to include
  2211. * in the signed URL.
  2212. * @param {string} [config.promptSaveAs] The filename to prompt the user to
  2213. * save the file as when the signed url is accessed. This is ignored if
  2214. * `config.responseDisposition` is set.
  2215. * @param {string} [config.responseDisposition] The
  2216. * {@link http://goo.gl/yMWxQV| response-content-disposition parameter} of the
  2217. * signed url.
  2218. * @param {*} [config.accessibleAt=Date.now()] A timestamp when this link became usable. Any value
  2219. * given is passed to `new Date()`.
  2220. * Note: Use for 'v4' only.
  2221. * @param {string} [config.responseType] The response-content-type parameter
  2222. * of the signed url.
  2223. * @param {GetSignedUrlCallback} [callback] Callback function.
  2224. * @returns {Promise<GetSignedUrlResponse>}
  2225. *
  2226. * @example
  2227. * ```
  2228. * const {Storage} = require('@google-cloud/storage');
  2229. * const storage = new Storage();
  2230. * const myBucket = storage.bucket('my-bucket');
  2231. *
  2232. * const file = myBucket.file('my-file');
  2233. *
  2234. * //-
  2235. * // Generate a URL that allows temporary access to download your file.
  2236. * //-
  2237. * const request = require('request');
  2238. *
  2239. * const config = {
  2240. * action: 'read',
  2241. * expires: '03-17-2025',
  2242. * };
  2243. *
  2244. * file.getSignedUrl(config, function(err, url) {
  2245. * if (err) {
  2246. * console.error(err);
  2247. * return;
  2248. * }
  2249. *
  2250. * // The file is now available to read from this URL.
  2251. * request(url, function(err, resp) {
  2252. * // resp.statusCode = 200
  2253. * });
  2254. * });
  2255. *
  2256. * //-
  2257. * // Generate a URL that allows temporary access to download your file.
  2258. * // Access will begin at accessibleAt and end at expires.
  2259. * //-
  2260. * const request = require('request');
  2261. *
  2262. * const config = {
  2263. * action: 'read',
  2264. * expires: '03-17-2025',
  2265. * accessibleAt: '03-13-2025'
  2266. * };
  2267. *
  2268. * file.getSignedUrl(config, function(err, url) {
  2269. * if (err) {
  2270. * console.error(err);
  2271. * return;
  2272. * }
  2273. *
  2274. * // The file will be available to read from this URL from 03-13-2025 to 03-17-2025.
  2275. * request(url, function(err, resp) {
  2276. * // resp.statusCode = 200
  2277. * });
  2278. * });
  2279. *
  2280. * //-
  2281. * // Generate a URL to allow write permissions. This means anyone with this
  2282. * URL
  2283. * // can send a POST request with new data that will overwrite the file.
  2284. * //-
  2285. * file.getSignedUrl({
  2286. * action: 'write',
  2287. * expires: '03-17-2025'
  2288. * }, function(err, url) {
  2289. * if (err) {
  2290. * console.error(err);
  2291. * return;
  2292. * }
  2293. *
  2294. * // The file is now available to be written to.
  2295. * const writeStream = request.put(url);
  2296. * writeStream.end('New data');
  2297. *
  2298. * writeStream.on('complete', function(resp) {
  2299. * // Confirm the new content was saved.
  2300. * file.download(function(err, fileContents) {
  2301. * console.log('Contents:', fileContents.toString());
  2302. * // Contents: New data
  2303. * });
  2304. * });
  2305. * });
  2306. *
  2307. * //-
  2308. * // If the callback is omitted, we'll return a Promise.
  2309. * //-
  2310. * file.getSignedUrl(config).then(function(data) {
  2311. * const url = data[0];
  2312. * });
  2313. *
  2314. * ```
  2315. * @example <caption>include:samples/files.js</caption>
  2316. * region_tag:storage_generate_signed_url
  2317. * Another example:
  2318. */
  2319. getSignedUrl(cfg, callback) {
  2320. const method = ActionToHTTPMethod[cfg.action];
  2321. const extensionHeaders = objectKeyToLowercase(cfg.extensionHeaders || {});
  2322. if (cfg.action === 'resumable') {
  2323. extensionHeaders['x-goog-resumable'] = 'start';
  2324. }
  2325. const queryParams = Object.assign({}, cfg.queryParams);
  2326. if (typeof cfg.responseType === 'string') {
  2327. queryParams['response-content-type'] = cfg.responseType;
  2328. }
  2329. if (typeof cfg.promptSaveAs === 'string') {
  2330. queryParams['response-content-disposition'] =
  2331. 'attachment; filename="' + cfg.promptSaveAs + '"';
  2332. }
  2333. if (typeof cfg.responseDisposition === 'string') {
  2334. queryParams['response-content-disposition'] = cfg.responseDisposition;
  2335. }
  2336. if (this.generation) {
  2337. queryParams['generation'] = this.generation.toString();
  2338. }
  2339. const signConfig = {
  2340. method,
  2341. expires: cfg.expires,
  2342. accessibleAt: cfg.accessibleAt,
  2343. extensionHeaders,
  2344. queryParams,
  2345. contentMd5: cfg.contentMd5,
  2346. contentType: cfg.contentType,
  2347. host: cfg.host,
  2348. };
  2349. if (cfg.cname) {
  2350. signConfig.cname = cfg.cname;
  2351. }
  2352. if (cfg.version) {
  2353. signConfig.version = cfg.version;
  2354. }
  2355. if (cfg.virtualHostedStyle) {
  2356. signConfig.virtualHostedStyle = cfg.virtualHostedStyle;
  2357. }
  2358. if (!this.signer) {
  2359. this.signer = new URLSigner(this.storage.authClient, this.bucket, this, this.storage);
  2360. }
  2361. this.signer
  2362. .getSignedUrl(signConfig)
  2363. .then(signedUrl => callback(null, signedUrl), callback);
  2364. }
  2365. /**
  2366. * @callback IsPublicCallback
  2367. * @param {?Error} err Request error, if any.
  2368. * @param {boolean} resp Whether file is public or not.
  2369. */
  2370. /**
  2371. * @typedef {array} IsPublicResponse
  2372. * @property {boolean} 0 Whether file is public or not.
  2373. */
  2374. /**
  2375. * Check whether this file is public or not by sending
  2376. * a HEAD request without credentials.
  2377. * No errors from the server indicates that the current
  2378. * file is public.
  2379. * A 403-Forbidden error {@link https://cloud.google.com/storage/docs/json_api/v1/status-codes#403_Forbidden}
  2380. * indicates that file is private.
  2381. * Any other non 403 error is propagated to user.
  2382. *
  2383. * @param {IsPublicCallback} [callback] Callback function.
  2384. * @returns {Promise<IsPublicResponse>}
  2385. *
  2386. * @example
  2387. * ```
  2388. * const {Storage} = require('@google-cloud/storage');
  2389. * const storage = new Storage();
  2390. * const myBucket = storage.bucket('my-bucket');
  2391. *
  2392. * const file = myBucket.file('my-file');
  2393. *
  2394. * //-
  2395. * // Check whether the file is publicly accessible.
  2396. * //-
  2397. * file.isPublic(function(err, resp) {
  2398. * if (err) {
  2399. * console.error(err);
  2400. * return;
  2401. * }
  2402. * console.log(`the file ${file.id} is public: ${resp}`) ;
  2403. * })
  2404. * //-
  2405. * // If the callback is omitted, we'll return a Promise.
  2406. * //-
  2407. * file.isPublic().then(function(data) {
  2408. * const resp = data[0];
  2409. * });
  2410. * ```
  2411. */
  2412. isPublic(callback) {
  2413. var _a;
  2414. // Build any custom headers based on the defined interceptors on the parent
  2415. // storage object and this object
  2416. const storageInterceptors = ((_a = this.storage) === null || _a === void 0 ? void 0 : _a.interceptors) || [];
  2417. const fileInterceptors = this.interceptors || [];
  2418. const allInterceptors = storageInterceptors.concat(fileInterceptors);
  2419. const headers = allInterceptors.reduce((acc, curInterceptor) => {
  2420. const currentHeaders = curInterceptor.request({
  2421. uri: `${this.storage.apiEndpoint}/${this.bucket.name}/${encodeURIComponent(this.name)}`,
  2422. });
  2423. Object.assign(acc, currentHeaders.headers);
  2424. return acc;
  2425. }, {});
  2426. util.makeRequest({
  2427. method: 'GET',
  2428. uri: `${this.storage.apiEndpoint}/${this.bucket.name}/${encodeURIComponent(this.name)}`,
  2429. headers,
  2430. }, {
  2431. retryOptions: this.storage.retryOptions,
  2432. }, (err) => {
  2433. if (err) {
  2434. const apiError = err;
  2435. if (apiError.code === 403) {
  2436. callback(null, false);
  2437. }
  2438. else {
  2439. callback(err);
  2440. }
  2441. }
  2442. else {
  2443. callback(null, true);
  2444. }
  2445. });
  2446. }
  2447. /**
  2448. * @typedef {object} MakeFilePrivateOptions Configuration options for File#makePrivate().
  2449. * @property {Metadata} [metadata] Define custom metadata properties to define
  2450. * along with the operation.
  2451. * @property {boolean} [strict] If true, set the file to be private to
  2452. * only the owner user. Otherwise, it will be private to the project.
  2453. * @property {string} [userProject] The ID of the project which will be
  2454. * billed for the request.
  2455. */
  2456. /**
  2457. * @callback MakeFilePrivateCallback
  2458. * @param {?Error} err Request error, if any.
  2459. * @param {object} apiResponse The full API response.
  2460. */
  2461. /**
  2462. * @typedef {array} MakeFilePrivateResponse
  2463. * @property {object} 0 The full API response.
  2464. */
  2465. /**
  2466. * Make a file private to the project and remove all other permissions.
  2467. * Set `options.strict` to true to make the file private to only the owner.
  2468. *
  2469. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objects/patch| Objects: patch API Documentation}
  2470. *
  2471. * @param {MakeFilePrivateOptions} [options] Configuration options.
  2472. * @param {MakeFilePrivateCallback} [callback] Callback function.
  2473. * @returns {Promise<MakeFilePrivateResponse>}
  2474. *
  2475. * @example
  2476. * ```
  2477. * const {Storage} = require('@google-cloud/storage');
  2478. * const storage = new Storage();
  2479. * const myBucket = storage.bucket('my-bucket');
  2480. *
  2481. * const file = myBucket.file('my-file');
  2482. *
  2483. * //-
  2484. * // Set the file private so only project maintainers can see and modify it.
  2485. * //-
  2486. * file.makePrivate(function(err) {});
  2487. *
  2488. * //-
  2489. * // Set the file private so only the owner can see and modify it.
  2490. * //-
  2491. * file.makePrivate({ strict: true }, function(err) {});
  2492. *
  2493. * //-
  2494. * // If the callback is omitted, we'll return a Promise.
  2495. * //-
  2496. * file.makePrivate().then(function(data) {
  2497. * const apiResponse = data[0];
  2498. * });
  2499. * ```
  2500. */
  2501. makePrivate(optionsOrCallback, callback) {
  2502. var _a, _b;
  2503. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  2504. callback =
  2505. typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
  2506. const query = {
  2507. predefinedAcl: options.strict ? 'private' : 'projectPrivate',
  2508. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  2509. };
  2510. if (((_a = options.preconditionOpts) === null || _a === void 0 ? void 0 : _a.ifMetagenerationMatch) !== undefined) {
  2511. query.ifMetagenerationMatch =
  2512. (_b = options.preconditionOpts) === null || _b === void 0 ? void 0 : _b.ifMetagenerationMatch;
  2513. delete options.preconditionOpts;
  2514. }
  2515. if (options.userProject) {
  2516. query.userProject = options.userProject;
  2517. }
  2518. // You aren't allowed to set both predefinedAcl & acl properties on a file,
  2519. // so acl must explicitly be nullified, destroying all previous acls on the
  2520. // file.
  2521. const metadata = { ...options.metadata, acl: null };
  2522. this.setMetadata(metadata, query, callback);
  2523. }
  2524. /**
  2525. * @typedef {array} MakeFilePublicResponse
  2526. * @property {object} 0 The full API response.
  2527. */
  2528. /**
  2529. * @callback MakeFilePublicCallback
  2530. * @param {?Error} err Request error, if any.
  2531. * @param {object} apiResponse The full API response.
  2532. */
  2533. /**
  2534. * Set a file to be publicly readable and maintain all previous permissions.
  2535. *
  2536. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/insert| ObjectAccessControls: insert API Documentation}
  2537. *
  2538. * @param {MakeFilePublicCallback} [callback] Callback function.
  2539. * @returns {Promise<MakeFilePublicResponse>}
  2540. *
  2541. * @example
  2542. * ```
  2543. * const {Storage} = require('@google-cloud/storage');
  2544. * const storage = new Storage();
  2545. * const myBucket = storage.bucket('my-bucket');
  2546. *
  2547. * const file = myBucket.file('my-file');
  2548. *
  2549. * file.makePublic(function(err, apiResponse) {});
  2550. *
  2551. * //-
  2552. * // If the callback is omitted, we'll return a Promise.
  2553. * //-
  2554. * file.makePublic().then(function(data) {
  2555. * const apiResponse = data[0];
  2556. * });
  2557. *
  2558. * ```
  2559. * @example <caption>include:samples/files.js</caption>
  2560. * region_tag:storage_make_public
  2561. * Another example:
  2562. */
  2563. makePublic(callback) {
  2564. callback = callback || util.noop;
  2565. this.acl.add({
  2566. entity: 'allUsers',
  2567. role: 'READER',
  2568. }, (err, acl, resp) => {
  2569. callback(err, resp);
  2570. });
  2571. }
  2572. /**
  2573. * The public URL of this File
  2574. * Use {@link File#makePublic} to enable anonymous access via the returned URL.
  2575. *
  2576. * @returns {string}
  2577. *
  2578. * @example
  2579. * ```
  2580. * const {Storage} = require('@google-cloud/storage');
  2581. * const storage = new Storage();
  2582. * const bucket = storage.bucket('albums');
  2583. * const file = bucket.file('my-file');
  2584. *
  2585. * // publicUrl will be "https://storage.googleapis.com/albums/my-file"
  2586. * const publicUrl = file.publicUrl();
  2587. * ```
  2588. */
  2589. publicUrl() {
  2590. return `${this.storage.apiEndpoint}/${this.bucket.name}/${encodeURIComponent(this.name)}`;
  2591. }
  2592. /**
  2593. * @typedef {array} MoveResponse
  2594. * @property {File} 0 The destination File.
  2595. * @property {object} 1 The full API response.
  2596. */
  2597. /**
  2598. * @callback MoveCallback
  2599. * @param {?Error} err Request error, if any.
  2600. * @param {?File} destinationFile The destination File.
  2601. * @param {object} apiResponse The full API response.
  2602. */
  2603. /**
  2604. * @typedef {object} MoveOptions Configuration options for File#move(). See an
  2605. * {@link https://cloud.google.com/storage/docs/json_api/v1/objects#resource| Object resource}.
  2606. * @param {string} [userProject] The ID of the project which will be
  2607. * billed for the request.
  2608. */
  2609. /**
  2610. * Move this file to another location. By default, this will rename the file
  2611. * and keep it in the same bucket, but you can choose to move it to another
  2612. * Bucket by providing a Bucket or File object or a URL beginning with
  2613. * "gs://".
  2614. *
  2615. * **Warning**:
  2616. * There is currently no atomic `move` method in the Cloud Storage API,
  2617. * so this method is a composition of {@link File#copy} (to the new
  2618. * location) and {@link File#delete} (from the old location). While
  2619. * unlikely, it is possible that an error returned to your callback could be
  2620. * triggered from either one of these API calls failing, which could leave a
  2621. * duplicate file lingering. The error message will indicate what operation
  2622. * has failed.
  2623. *
  2624. * See {@link https://cloud.google.com/storage/docs/json_api/v1/objects/copy| Objects: copy API Documentation}
  2625. *
  2626. * @throws {Error} If the destination file is not provided.
  2627. *
  2628. * @param {string|Bucket|File} destination Destination file.
  2629. * @param {MoveCallback} [callback] Callback function.
  2630. * @returns {Promise<MoveResponse>}
  2631. *
  2632. * @example
  2633. * ```
  2634. * const {Storage} = require('@google-cloud/storage');
  2635. * const storage = new Storage();
  2636. * //-
  2637. * // You can pass in a variety of types for the destination.
  2638. * //
  2639. * // For all of the below examples, assume we are working with the following
  2640. * // Bucket and File objects.
  2641. * //-
  2642. * const bucket = storage.bucket('my-bucket');
  2643. * const file = bucket.file('my-image.png');
  2644. *
  2645. * //-
  2646. * // If you pass in a string for the destination, the file is moved to its
  2647. * // current bucket, under the new name provided.
  2648. * //-
  2649. * file.move('my-image-new.png', function(err, destinationFile, apiResponse) {
  2650. * // `my-bucket` no longer contains:
  2651. * // - "my-image.png"
  2652. * // but contains instead:
  2653. * // - "my-image-new.png"
  2654. *
  2655. * // `destinationFile` is an instance of a File object that refers to your
  2656. * // new file.
  2657. * });
  2658. *
  2659. * //-
  2660. * // If you pass in a string starting with "gs://" for the destination, the
  2661. * // file is copied to the other bucket and under the new name provided.
  2662. * //-
  2663. * const newLocation = 'gs://another-bucket/my-image-new.png';
  2664. * file.move(newLocation, function(err, destinationFile, apiResponse) {
  2665. * // `my-bucket` no longer contains:
  2666. * // - "my-image.png"
  2667. * //
  2668. * // `another-bucket` now contains:
  2669. * // - "my-image-new.png"
  2670. *
  2671. * // `destinationFile` is an instance of a File object that refers to your
  2672. * // new file.
  2673. * });
  2674. *
  2675. * //-
  2676. * // If you pass in a Bucket object, the file will be moved to that bucket
  2677. * // using the same name.
  2678. * //-
  2679. * const anotherBucket = gcs.bucket('another-bucket');
  2680. *
  2681. * file.move(anotherBucket, function(err, destinationFile, apiResponse) {
  2682. * // `my-bucket` no longer contains:
  2683. * // - "my-image.png"
  2684. * //
  2685. * // `another-bucket` now contains:
  2686. * // - "my-image.png"
  2687. *
  2688. * // `destinationFile` is an instance of a File object that refers to your
  2689. * // new file.
  2690. * });
  2691. *
  2692. * //-
  2693. * // If you pass in a File object, you have complete control over the new
  2694. * // bucket and filename.
  2695. * //-
  2696. * const anotherFile = anotherBucket.file('my-awesome-image.png');
  2697. *
  2698. * file.move(anotherFile, function(err, destinationFile, apiResponse) {
  2699. * // `my-bucket` no longer contains:
  2700. * // - "my-image.png"
  2701. * //
  2702. * // `another-bucket` now contains:
  2703. * // - "my-awesome-image.png"
  2704. *
  2705. * // Note:
  2706. * // The `destinationFile` parameter is equal to `anotherFile`.
  2707. * });
  2708. *
  2709. * //-
  2710. * // If the callback is omitted, we'll return a Promise.
  2711. * //-
  2712. * file.move('my-image-new.png').then(function(data) {
  2713. * const destinationFile = data[0];
  2714. * const apiResponse = data[1];
  2715. * });
  2716. *
  2717. * ```
  2718. * @example <caption>include:samples/files.js</caption>
  2719. * region_tag:storage_move_file
  2720. * Another example:
  2721. */
  2722. move(destination, optionsOrCallback, callback) {
  2723. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  2724. callback =
  2725. typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
  2726. callback = callback || util.noop;
  2727. this.copy(destination, options, (err, destinationFile, copyApiResponse) => {
  2728. if (err) {
  2729. err.message = 'file#copy failed with an error - ' + err.message;
  2730. callback(err, null, copyApiResponse);
  2731. return;
  2732. }
  2733. if (this.name !== destinationFile.name ||
  2734. this.bucket.name !== destinationFile.bucket.name) {
  2735. this.delete(options, (err, apiResponse) => {
  2736. if (err) {
  2737. err.message = 'file#delete failed with an error - ' + err.message;
  2738. callback(err, destinationFile, apiResponse);
  2739. return;
  2740. }
  2741. callback(null, destinationFile, copyApiResponse);
  2742. });
  2743. }
  2744. else {
  2745. callback(null, destinationFile, copyApiResponse);
  2746. }
  2747. });
  2748. }
  2749. /**
  2750. * @typedef {array} RenameResponse
  2751. * @property {File} 0 The destination File.
  2752. * @property {object} 1 The full API response.
  2753. */
  2754. /**
  2755. * @callback RenameCallback
  2756. * @param {?Error} err Request error, if any.
  2757. * @param {?File} destinationFile The destination File.
  2758. * @param {object} apiResponse The full API response.
  2759. */
  2760. /**
  2761. * @typedef {object} RenameOptions Configuration options for File#move(). See an
  2762. * {@link https://cloud.google.com/storage/docs/json_api/v1/objects#resource| Object resource}.
  2763. * @param {string} [userProject] The ID of the project which will be
  2764. * billed for the request.
  2765. */
  2766. /**
  2767. * Rename this file.
  2768. *
  2769. * **Warning**:
  2770. * There is currently no atomic `rename` method in the Cloud Storage API,
  2771. * so this method is an alias of {@link File#move}, which in turn is a
  2772. * composition of {@link File#copy} (to the new location) and
  2773. * {@link File#delete} (from the old location). While
  2774. * unlikely, it is possible that an error returned to your callback could be
  2775. * triggered from either one of these API calls failing, which could leave a
  2776. * duplicate file lingering. The error message will indicate what operation
  2777. * has failed.
  2778. *
  2779. * @param {string|File} destinationFile Destination file.
  2780. * @param {RenameCallback} [callback] Callback function.
  2781. * @returns {Promise<RenameResponse>}
  2782. *
  2783. * @example
  2784. * ```
  2785. * const {Storage} = require('@google-cloud/storage');
  2786. * const storage = new Storage();
  2787. *
  2788. * //-
  2789. * // You can pass in a string or a File object.
  2790. * //
  2791. * // For all of the below examples, assume we are working with the following
  2792. * // Bucket and File objects.
  2793. * //-
  2794. *
  2795. * const bucket = storage.bucket('my-bucket');
  2796. * const file = bucket.file('my-image.png');
  2797. *
  2798. * //-
  2799. * // You can pass in a string for the destinationFile.
  2800. * //-
  2801. * file.rename('renamed-image.png', function(err, renamedFile, apiResponse) {
  2802. * // `my-bucket` no longer contains:
  2803. * // - "my-image.png"
  2804. * // but contains instead:
  2805. * // - "renamed-image.png"
  2806. *
  2807. * // `renamedFile` is an instance of a File object that refers to your
  2808. * // renamed file.
  2809. * });
  2810. *
  2811. * //-
  2812. * // You can pass in a File object.
  2813. * //-
  2814. * const anotherFile = anotherBucket.file('my-awesome-image.png');
  2815. *
  2816. * file.rename(anotherFile, function(err, renamedFile, apiResponse) {
  2817. * // `my-bucket` no longer contains:
  2818. * // - "my-image.png"
  2819. *
  2820. * // Note:
  2821. * // The `renamedFile` parameter is equal to `anotherFile`.
  2822. * });
  2823. *
  2824. * //-
  2825. * // If the callback is omitted, we'll return a Promise.
  2826. * //-
  2827. * file.rename('my-renamed-image.png').then(function(data) {
  2828. * const renamedFile = data[0];
  2829. * const apiResponse = data[1];
  2830. * });
  2831. * ```
  2832. */
  2833. rename(destinationFile, optionsOrCallback, callback) {
  2834. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  2835. callback =
  2836. typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
  2837. callback = callback || util.noop;
  2838. this.move(destinationFile, options, callback);
  2839. }
  2840. /**
  2841. * @typedef {object} RestoreOptions Options for File#restore(). See an
  2842. * {@link https://cloud.google.com/storage/docs/json_api/v1/objects#resource| Object resource}.
  2843. * @param {string} [userProject] The ID of the project which will be
  2844. * billed for the request.
  2845. * @param {number} [generation] If present, selects a specific revision of this object.
  2846. * @param {string} [restoreToken] Returns an option that must be specified when getting a soft-deleted object from an HNS-enabled
  2847. * bucket that has a naming and generation conflict with another object in the same bucket.
  2848. * @param {string} [projection] Specifies the set of properties to return. If used, must be 'full' or 'noAcl'.
  2849. * @param {string | number} [ifGenerationMatch] Request proceeds if the generation of the target resource
  2850. * matches the value used in the precondition.
  2851. * If the values don't match, the request fails with a 412 Precondition Failed response.
  2852. * @param {string | number} [ifGenerationNotMatch] Request proceeds if the generation of the target resource does
  2853. * not match the value used in the precondition. If the values match, the request fails with a 304 Not Modified response.
  2854. * @param {string | number} [ifMetagenerationMatch] Request proceeds if the meta-generation of the target resource
  2855. * matches the value used in the precondition.
  2856. * If the values don't match, the request fails with a 412 Precondition Failed response.
  2857. * @param {string | number} [ifMetagenerationNotMatch] Request proceeds if the meta-generation of the target resource does
  2858. * not match the value used in the precondition. If the values match, the request fails with a 304 Not Modified response.
  2859. */
  2860. /**
  2861. * Restores a soft-deleted file
  2862. * @param {RestoreOptions} options Restore options.
  2863. * @returns {Promise<File>}
  2864. */
  2865. async restore(options) {
  2866. const [file] = await this.request({
  2867. method: 'POST',
  2868. uri: '/restore',
  2869. qs: options,
  2870. });
  2871. return file;
  2872. }
  2873. /**
  2874. * Makes request and applies userProject query parameter if necessary.
  2875. *
  2876. * @private
  2877. *
  2878. * @param {object} reqOpts - The request options.
  2879. * @param {function} callback - The callback function.
  2880. */
  2881. request(reqOpts, callback) {
  2882. return this.parent.request.call(this, reqOpts, callback);
  2883. }
  2884. /**
  2885. * @callback RotateEncryptionKeyCallback
  2886. * @extends CopyCallback
  2887. */
  2888. /**
  2889. * @typedef RotateEncryptionKeyResponse
  2890. * @extends CopyResponse
  2891. */
  2892. /**
  2893. * @param {string|buffer|object} RotateEncryptionKeyOptions Configuration options
  2894. * for File#rotateEncryptionKey().
  2895. * If a string or Buffer is provided, it is interpreted as an AES-256,
  2896. * customer-supplied encryption key. If you'd like to use a Cloud KMS key
  2897. * name, you must specify an options object with the property name:
  2898. * `kmsKeyName`.
  2899. * @param {string|buffer} [options.encryptionKey] An AES-256 encryption key.
  2900. * @param {string} [options.kmsKeyName] A Cloud KMS key name.
  2901. */
  2902. /**
  2903. * This method allows you to update the encryption key associated with this
  2904. * file.
  2905. *
  2906. * See {@link https://cloud.google.com/storage/docs/encryption#customer-supplied| Customer-supplied Encryption Keys}
  2907. *
  2908. * @param {RotateEncryptionKeyOptions} [options] - Configuration options.
  2909. * @param {RotateEncryptionKeyCallback} [callback]
  2910. * @returns {Promise<File>}
  2911. *
  2912. * @example <caption>include:samples/encryption.js</caption>
  2913. * region_tag:storage_rotate_encryption_key
  2914. * Example of rotating the encryption key for this file:
  2915. */
  2916. rotateEncryptionKey(optionsOrCallback, callback) {
  2917. var _a;
  2918. callback =
  2919. typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
  2920. let options = {};
  2921. if (typeof optionsOrCallback === 'string' ||
  2922. optionsOrCallback instanceof Buffer) {
  2923. options = {
  2924. encryptionKey: optionsOrCallback,
  2925. };
  2926. }
  2927. else if (typeof optionsOrCallback === 'object') {
  2928. options = optionsOrCallback;
  2929. }
  2930. const newFile = this.bucket.file(this.id, options);
  2931. const copyOptions = ((_a = options.preconditionOpts) === null || _a === void 0 ? void 0 : _a.ifGenerationMatch) !== undefined
  2932. ? { preconditionOpts: options.preconditionOpts }
  2933. : {};
  2934. this.copy(newFile, copyOptions, callback);
  2935. }
  2936. /**
  2937. * @typedef {object} SaveOptions
  2938. * @extends CreateWriteStreamOptions
  2939. */
  2940. /**
  2941. * @callback SaveCallback
  2942. * @param {?Error} err Request error, if any.
  2943. */
  2944. /**
  2945. * Write strings or buffers to a file.
  2946. *
  2947. * *This is a convenience method which wraps {@link File#createWriteStream}.*
  2948. * To upload arbitrary data to a file, please use {@link File#createWriteStream} directly.
  2949. *
  2950. * Resumable uploads are automatically enabled and must be shut off explicitly
  2951. * by setting `options.resumable` to `false`.
  2952. *
  2953. * Multipart uploads with retryable error codes will be retried 3 times with exponential backoff.
  2954. *
  2955. * <p class="notice">
  2956. * There is some overhead when using a resumable upload that can cause
  2957. * noticeable performance degradation while uploading a series of small
  2958. * files. When uploading files less than 10MB, it is recommended that the
  2959. * resumable feature is disabled.
  2960. * </p>
  2961. *
  2962. * @param {SaveData} data The data to write to a file.
  2963. * @param {SaveOptions} [options] See {@link File#createWriteStream}'s `options`
  2964. * parameter.
  2965. * @param {SaveCallback} [callback] Callback function.
  2966. * @returns {Promise}
  2967. *
  2968. * @example
  2969. * ```
  2970. * const {Storage} = require('@google-cloud/storage');
  2971. * const storage = new Storage();
  2972. * const myBucket = storage.bucket('my-bucket');
  2973. *
  2974. * const file = myBucket.file('my-file');
  2975. * const contents = 'This is the contents of the file.';
  2976. *
  2977. * file.save(contents, function(err) {
  2978. * if (!err) {
  2979. * // File written successfully.
  2980. * }
  2981. * });
  2982. *
  2983. * //-
  2984. * // If the callback is omitted, we'll return a Promise.
  2985. * //-
  2986. * file.save(contents).then(function() {});
  2987. * ```
  2988. */
  2989. save(data, optionsOrCallback, callback) {
  2990. // tslint:enable:no-any
  2991. callback =
  2992. typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
  2993. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  2994. let maxRetries = this.storage.retryOptions.maxRetries;
  2995. if (!this.shouldRetryBasedOnPreconditionAndIdempotencyStrat(options === null || options === void 0 ? void 0 : options.preconditionOpts)) {
  2996. maxRetries = 0;
  2997. }
  2998. const returnValue = AsyncRetry(async (bail) => {
  2999. return new Promise((resolve, reject) => {
  3000. if (maxRetries === 0) {
  3001. this.storage.retryOptions.autoRetry = false;
  3002. }
  3003. const writable = this.createWriteStream(options);
  3004. if (options.onUploadProgress) {
  3005. writable.on('progress', options.onUploadProgress);
  3006. }
  3007. const handleError = (err) => {
  3008. if (this.storage.retryOptions.autoRetry &&
  3009. this.storage.retryOptions.retryableErrorFn(err)) {
  3010. return reject(err);
  3011. }
  3012. return bail(err);
  3013. };
  3014. if (typeof data === 'string' ||
  3015. Buffer.isBuffer(data) ||
  3016. data instanceof Uint8Array) {
  3017. writable
  3018. .on('error', handleError)
  3019. .on('finish', () => resolve())
  3020. .end(data);
  3021. }
  3022. else {
  3023. pipeline(data, writable, err => {
  3024. if (err) {
  3025. if (typeof data !== 'function') {
  3026. // Only PipelineSourceFunction can be retried. Async-iterables
  3027. // and Readable streams can only be consumed once.
  3028. return bail(err);
  3029. }
  3030. handleError(err);
  3031. }
  3032. else {
  3033. resolve();
  3034. }
  3035. });
  3036. }
  3037. });
  3038. }, {
  3039. retries: maxRetries,
  3040. factor: this.storage.retryOptions.retryDelayMultiplier,
  3041. maxTimeout: this.storage.retryOptions.maxRetryDelay * 1000, //convert to milliseconds
  3042. maxRetryTime: this.storage.retryOptions.totalTimeout * 1000, //convert to milliseconds
  3043. });
  3044. if (!callback) {
  3045. return returnValue;
  3046. }
  3047. else {
  3048. return returnValue
  3049. .then(() => {
  3050. if (callback) {
  3051. return callback();
  3052. }
  3053. })
  3054. .catch(callback);
  3055. }
  3056. }
  3057. setMetadata(metadata, optionsOrCallback, cb) {
  3058. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  3059. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  3060. cb =
  3061. typeof optionsOrCallback === 'function'
  3062. ? optionsOrCallback
  3063. : cb;
  3064. this.disableAutoRetryConditionallyIdempotent_(this.methods.setMetadata, AvailableServiceObjectMethods.setMetadata, options);
  3065. super
  3066. .setMetadata(metadata, options)
  3067. .then(resp => cb(null, ...resp))
  3068. .catch(cb)
  3069. .finally(() => {
  3070. this.storage.retryOptions.autoRetry = this.instanceRetryValue;
  3071. });
  3072. }
  3073. /**
  3074. * @typedef {array} SetStorageClassResponse
  3075. * @property {object} 0 The full API response.
  3076. */
  3077. /**
  3078. * @typedef {object} SetStorageClassOptions Configuration options for File#setStorageClass().
  3079. * @property {string} [userProject] The ID of the project which will be
  3080. * billed for the request.
  3081. */
  3082. /**
  3083. * @callback SetStorageClassCallback
  3084. * @param {?Error} err Request error, if any.
  3085. * @param {object} apiResponse The full API response.
  3086. */
  3087. /**
  3088. * Set the storage class for this file.
  3089. *
  3090. * See {@link https://cloud.google.com/storage/docs/per-object-storage-class| Per-Object Storage Class}
  3091. * See {@link https://cloud.google.com/storage/docs/storage-classes| Storage Classes}
  3092. *
  3093. * @param {string} storageClass The new storage class. (`standard`,
  3094. * `nearline`, `coldline`, or `archive`)
  3095. * **Note:** The storage classes `multi_regional` and `regional`
  3096. * are now legacy and will be deprecated in the future.
  3097. * @param {SetStorageClassOptions} [options] Configuration options.
  3098. * @param {string} [options.userProject] The ID of the project which will be
  3099. * billed for the request.
  3100. * @param {SetStorageClassCallback} [callback] Callback function.
  3101. * @returns {Promise<SetStorageClassResponse>}
  3102. *
  3103. * @example
  3104. * ```
  3105. * file.setStorageClass('nearline', function(err, apiResponse) {
  3106. * if (err) {
  3107. * // Error handling omitted.
  3108. * }
  3109. *
  3110. * // The storage class was updated successfully.
  3111. * });
  3112. *
  3113. * //-
  3114. * // If the callback is omitted, we'll return a Promise.
  3115. * //-
  3116. * file.setStorageClass('nearline').then(function() {});
  3117. * ```
  3118. */
  3119. setStorageClass(storageClass, optionsOrCallback, callback) {
  3120. callback =
  3121. typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
  3122. const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
  3123. const req = {
  3124. ...options,
  3125. // In case we get input like `storageClass`, convert to `storage_class`.
  3126. storageClass: storageClass
  3127. .replace(/-/g, '_')
  3128. .replace(/([a-z])([A-Z])/g, (_, low, up) => {
  3129. return low + '_' + up;
  3130. })
  3131. .toUpperCase(),
  3132. };
  3133. this.copy(this, req, (err, file, apiResponse) => {
  3134. if (err) {
  3135. callback(err, apiResponse);
  3136. return;
  3137. }
  3138. this.metadata = file.metadata;
  3139. callback(null, apiResponse);
  3140. });
  3141. }
  3142. /**
  3143. * Set a user project to be billed for all requests made from this File
  3144. * object.
  3145. *
  3146. * @param {string} userProject The user project.
  3147. *
  3148. * @example
  3149. * ```
  3150. * const {Storage} = require('@google-cloud/storage');
  3151. * const storage = new Storage();
  3152. * const bucket = storage.bucket('albums');
  3153. * const file = bucket.file('my-file');
  3154. *
  3155. * file.setUserProject('grape-spaceship-123');
  3156. * ```
  3157. */
  3158. setUserProject(userProject) {
  3159. this.bucket.setUserProject.call(this, userProject);
  3160. }
  3161. /**
  3162. * This creates a resumable-upload upload stream.
  3163. *
  3164. * @param {Duplexify} stream - Duplexify stream of data to pipe to the file.
  3165. * @param {object=} options - Configuration object.
  3166. *
  3167. * @private
  3168. */
  3169. startResumableUpload_(dup, options = {}) {
  3170. var _a;
  3171. (_a = options.metadata) !== null && _a !== void 0 ? _a : (options.metadata = {});
  3172. const retryOptions = this.storage.retryOptions;
  3173. if (!this.shouldRetryBasedOnPreconditionAndIdempotencyStrat(options.preconditionOpts)) {
  3174. retryOptions.autoRetry = false;
  3175. }
  3176. const cfg = {
  3177. authClient: this.storage.authClient,
  3178. apiEndpoint: this.storage.apiEndpoint,
  3179. bucket: this.bucket.name,
  3180. customRequestOptions: this.getRequestInterceptors().reduce((reqOpts, interceptorFn) => interceptorFn(reqOpts), {}),
  3181. file: this.name,
  3182. generation: this.generation,
  3183. isPartialUpload: options.isPartialUpload,
  3184. key: this.encryptionKey,
  3185. kmsKeyName: this.kmsKeyName,
  3186. metadata: options.metadata,
  3187. offset: options.offset,
  3188. predefinedAcl: options.predefinedAcl,
  3189. private: options.private,
  3190. public: options.public,
  3191. uri: options.uri,
  3192. userProject: options.userProject || this.userProject,
  3193. retryOptions: { ...retryOptions },
  3194. params: (options === null || options === void 0 ? void 0 : options.preconditionOpts) || this.instancePreconditionOpts,
  3195. chunkSize: options === null || options === void 0 ? void 0 : options.chunkSize,
  3196. highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
  3197. universeDomain: this.bucket.storage.universeDomain,
  3198. [GCCL_GCS_CMD_KEY]: options[GCCL_GCS_CMD_KEY],
  3199. };
  3200. let uploadStream;
  3201. try {
  3202. uploadStream = resumableUpload.upload(cfg);
  3203. }
  3204. catch (error) {
  3205. dup.destroy(error);
  3206. this.storage.retryOptions.autoRetry = this.instanceRetryValue;
  3207. return;
  3208. }
  3209. uploadStream
  3210. .on('response', resp => {
  3211. dup.emit('response', resp);
  3212. })
  3213. .on('uri', uri => {
  3214. dup.emit('uri', uri);
  3215. })
  3216. .on('metadata', metadata => {
  3217. this.metadata = metadata;
  3218. dup.emit('metadata');
  3219. })
  3220. .on('finish', () => {
  3221. dup.emit('complete');
  3222. })
  3223. .on('progress', evt => dup.emit('progress', evt));
  3224. dup.setWritable(uploadStream);
  3225. this.storage.retryOptions.autoRetry = this.instanceRetryValue;
  3226. }
  3227. /**
  3228. * Takes a readable stream and pipes it to a remote file. Unlike
  3229. * `startResumableUpload_`, which uses the resumable upload technique, this
  3230. * method uses a simple upload (all or nothing).
  3231. *
  3232. * @param {Duplexify} dup - Duplexify stream of data to pipe to the file.
  3233. * @param {object=} options - Configuration object.
  3234. *
  3235. * @private
  3236. */
  3237. startSimpleUpload_(dup, options = {}) {
  3238. var _a;
  3239. (_a = options.metadata) !== null && _a !== void 0 ? _a : (options.metadata = {});
  3240. const apiEndpoint = this.storage.apiEndpoint;
  3241. const bucketName = this.bucket.name;
  3242. const uri = `${apiEndpoint}/upload/storage/v1/b/${bucketName}/o`;
  3243. const reqOpts = {
  3244. qs: {
  3245. name: this.name,
  3246. },
  3247. uri: uri,
  3248. [GCCL_GCS_CMD_KEY]: options[GCCL_GCS_CMD_KEY],
  3249. };
  3250. if (this.generation !== undefined) {
  3251. reqOpts.qs.ifGenerationMatch = this.generation;
  3252. }
  3253. if (this.kmsKeyName !== undefined) {
  3254. reqOpts.qs.kmsKeyName = this.kmsKeyName;
  3255. }
  3256. if (typeof options.timeout === 'number') {
  3257. reqOpts.timeout = options.timeout;
  3258. }
  3259. if (options.userProject || this.userProject) {
  3260. reqOpts.qs.userProject = options.userProject || this.userProject;
  3261. }
  3262. if (options.predefinedAcl) {
  3263. reqOpts.qs.predefinedAcl = options.predefinedAcl;
  3264. }
  3265. else if (options.private) {
  3266. reqOpts.qs.predefinedAcl = 'private';
  3267. }
  3268. else if (options.public) {
  3269. reqOpts.qs.predefinedAcl = 'publicRead';
  3270. }
  3271. Object.assign(reqOpts.qs, this.instancePreconditionOpts, options.preconditionOpts);
  3272. util.makeWritableStream(dup, {
  3273. makeAuthenticatedRequest: (reqOpts) => {
  3274. this.request(reqOpts, (err, body, resp) => {
  3275. if (err) {
  3276. dup.destroy(err);
  3277. return;
  3278. }
  3279. this.metadata = body;
  3280. dup.emit('metadata', body);
  3281. dup.emit('response', resp);
  3282. dup.emit('complete');
  3283. });
  3284. },
  3285. metadata: options.metadata,
  3286. request: reqOpts,
  3287. });
  3288. }
  3289. disableAutoRetryConditionallyIdempotent_(
  3290. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  3291. coreOpts, methodType, localPreconditionOptions) {
  3292. var _a, _b, _c, _d;
  3293. if ((typeof coreOpts === 'object' &&
  3294. ((_b = (_a = coreOpts === null || coreOpts === void 0 ? void 0 : coreOpts.reqOpts) === null || _a === void 0 ? void 0 : _a.qs) === null || _b === void 0 ? void 0 : _b.ifGenerationMatch) === undefined &&
  3295. (localPreconditionOptions === null || localPreconditionOptions === void 0 ? void 0 : localPreconditionOptions.ifGenerationMatch) === undefined &&
  3296. methodType === AvailableServiceObjectMethods.delete &&
  3297. this.storage.retryOptions.idempotencyStrategy ===
  3298. IdempotencyStrategy.RetryConditional) ||
  3299. this.storage.retryOptions.idempotencyStrategy ===
  3300. IdempotencyStrategy.RetryNever) {
  3301. this.storage.retryOptions.autoRetry = false;
  3302. }
  3303. if ((typeof coreOpts === 'object' &&
  3304. ((_d = (_c = coreOpts === null || coreOpts === void 0 ? void 0 : coreOpts.reqOpts) === null || _c === void 0 ? void 0 : _c.qs) === null || _d === void 0 ? void 0 : _d.ifMetagenerationMatch) === undefined &&
  3305. (localPreconditionOptions === null || localPreconditionOptions === void 0 ? void 0 : localPreconditionOptions.ifMetagenerationMatch) === undefined &&
  3306. methodType === AvailableServiceObjectMethods.setMetadata &&
  3307. this.storage.retryOptions.idempotencyStrategy ===
  3308. IdempotencyStrategy.RetryConditional) ||
  3309. this.storage.retryOptions.idempotencyStrategy ===
  3310. IdempotencyStrategy.RetryNever) {
  3311. this.storage.retryOptions.autoRetry = false;
  3312. }
  3313. }
  3314. async getBufferFromReadable(readable) {
  3315. const buf = [];
  3316. for await (const chunk of readable) {
  3317. buf.push(chunk);
  3318. }
  3319. return Buffer.concat(buf);
  3320. }
  3321. }
  3322. _File_instances = new WeakSet(), _File_validateIntegrity =
  3323. /**
  3324. *
  3325. * @param hashCalculatingStream
  3326. * @param verify
  3327. * @returns {boolean} Returns `true` if valid, throws with error otherwise
  3328. */
  3329. async function _File_validateIntegrity(hashCalculatingStream, verify = {}) {
  3330. const metadata = this.metadata;
  3331. // If we're doing validation, assume the worst
  3332. let dataMismatch = !!(verify.crc32c || verify.md5);
  3333. if (verify.crc32c && metadata.crc32c) {
  3334. dataMismatch = !hashCalculatingStream.test('crc32c', metadata.crc32c);
  3335. }
  3336. if (verify.md5 && metadata.md5Hash) {
  3337. dataMismatch = !hashCalculatingStream.test('md5', metadata.md5Hash);
  3338. }
  3339. if (dataMismatch) {
  3340. const errors = [];
  3341. let code = '';
  3342. let message = '';
  3343. try {
  3344. await this.delete();
  3345. if (verify.md5 && !metadata.md5Hash) {
  3346. code = 'MD5_NOT_AVAILABLE';
  3347. message = FileExceptionMessages.MD5_NOT_AVAILABLE;
  3348. }
  3349. else {
  3350. code = 'FILE_NO_UPLOAD';
  3351. message = FileExceptionMessages.UPLOAD_MISMATCH;
  3352. }
  3353. }
  3354. catch (e) {
  3355. const error = e;
  3356. code = 'FILE_NO_UPLOAD_DELETE';
  3357. message = `${FileExceptionMessages.UPLOAD_MISMATCH_DELETE_FAIL}${error.message}`;
  3358. errors.push(error);
  3359. }
  3360. const error = new RequestError(message);
  3361. error.code = code;
  3362. error.errors = errors;
  3363. throw error;
  3364. }
  3365. return true;
  3366. };
  3367. /*! Developer Documentation
  3368. *
  3369. * All async methods (except for streams) will return a Promise in the event
  3370. * that a callback is omitted.
  3371. */
  3372. promisifyAll(File, {
  3373. exclude: [
  3374. 'cloudStorageURI',
  3375. 'publicUrl',
  3376. 'request',
  3377. 'save',
  3378. 'setEncryptionKey',
  3379. 'shouldRetryBasedOnPreconditionAndIdempotencyStrat',
  3380. 'getBufferFromReadable',
  3381. 'restore',
  3382. ],
  3383. });
  3384. /**
  3385. * Reference to the {@link File} class.
  3386. * @name module:@google-cloud/storage.File
  3387. * @see File
  3388. */
  3389. export { File };