UNPKG

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