diff --git a/src/file.ts b/src/file.ts index 1df546b93..0a5acf2bc 100644 --- a/src/file.ts +++ b/src/file.ts @@ -479,6 +479,9 @@ export class RequestError extends Error { } const SEVEN_DAYS = 7 * 24 * 60 * 60; +const GS_UTIL_URL_REGEX = /(gs):\/\/([a-z0-9_.-]+)\/(.+)/g; +const HTTPS_PUBLIC_URL_REGEX = + /(https):\/\/(storage\.googleapis\.com)\/([a-z0-9_.-]+)\/(.+)/g; export enum FileExceptionMessages { EXPIRATION_TIME_NA = 'An expiration time is not available.', @@ -2358,6 +2361,35 @@ class File extends ServiceObject { return this; } + /** + * Gets a reference to a Cloud Storage {@link File} file from the provided URL in string format. + * @param {string} publicUrlOrGsUrl the URL as a string. Must be of the format gs://bucket/file + * or https://storage.googleapis.com/bucket/file. + * @param {Storage} storageInstance an instance of a Storage object. + * @param {FileOptions} [options] Configuration options + * @returns {File} + */ + static from( + publicUrlOrGsUrl: string, + storageInstance: Storage, + options?: FileOptions + ): File { + const gsMatches = [...publicUrlOrGsUrl.matchAll(GS_UTIL_URL_REGEX)]; + const httpsMatches = [...publicUrlOrGsUrl.matchAll(HTTPS_PUBLIC_URL_REGEX)]; + + if (gsMatches.length > 0) { + const bucket = new Bucket(storageInstance, gsMatches[0][1]); + return new File(bucket, gsMatches[0][2], options); + } else if (httpsMatches.length > 0) { + const bucket = new Bucket(storageInstance, httpsMatches[0][2]); + return new File(bucket, httpsMatches[0][3], options); + } else { + throw new Error( + 'URL string must be of format gs://bucket/file or https://storage.googleapis.com/bucket/file' + ); + } + } + get(options?: GetFileOptions): Promise>; get(callback: InstanceResponseCallback): void; get(options: GetFileOptions, callback: InstanceResponseCallback): void; diff --git a/test/file.ts b/test/file.ts index 73de841dd..d0cc0c2ba 100644 --- a/test/file.ts +++ b/test/file.ts @@ -5168,4 +5168,49 @@ describe('File', () => { file.setUserProject(userProject); }); }); + + describe('from', () => { + it('should create a File object from a gs:// formatted URL', () => { + const gsUrl = 'gs://mybucket/myfile'; + const result = File.from(gsUrl, STORAGE); + + assert(result); + assert(result.bucket.name, 'mybucket'); + assert(result.name, 'myfile'); + }); + + it('should create a File object from a gs:// formatted URL including a folder', () => { + const gsUrl = 'gs://mybucket/myfolder/myfile'; + const result = File.from(gsUrl, STORAGE); + + assert(result); + assert(result.bucket.name, 'mybucket'); + assert(result.name, 'myfolder/myfile'); + }); + + it('should create a File object from a https:// formatted URL', () => { + const httpsUrl = 'https://storage.googleapis.com/mybucket/myfile'; + const result = File.from(httpsUrl, STORAGE); + + assert(result); + assert(result.bucket.name, 'mybucket'); + assert(result.name, 'myfile'); + }); + + it('should create a File object from a https:// formatted URL including a folder', () => { + const httpsUrl = + 'https://storage.googleapis.com/mybucket/myfolder/myfile'; + const result = File.from(httpsUrl, STORAGE); + + assert(result); + assert(result.bucket.name, 'mybucket'); + assert(result.name, 'myfolder/myfile'); + }); + + it('should throw an error when invoked with an incorrectly formatted URL', () => { + const invalidUrl = 'https://storage.com/mybucket/myfile'; + + assert.throws(() => File.from(invalidUrl, STORAGE)); + }); + }); });