UNPKG

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