Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions samples/renameFile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* This application demonstrates how to perform basic operations on files with
* the Google Cloud Storage API.
*
* For more information, see the README.md under /storage and the documentation
* at https://cloud.google.com/storage/docs.
*/

function main(
bucketName = 'my-bucket',
srcFilename = 'test2.txt',
destFilename = 'test4.txt'
) {
// [START storage_rename_file]
/**
* TODO(developer): Uncomment the following lines before running the sample.
*/
// const bucketName = 'Name of a bucket, e.g. my-bucket';
// const srcFilename = 'File to rename, e.g. file.txt';
// const destFilename = 'Destination for file, e.g. renamed.txt';

// Imports the Google Cloud client library
const {Storage} = require('@google-cloud/storage');

// Creates a client
const storage = new Storage();

async function renameFile() {
// renames the file
await storage.bucket(bucketName).file(srcFilename).rename(destFilename);

console.log(
`gs://${bucketName}/${srcFilename} renamed to gs://${bucketName}/${destFilename}.`
);
}

renameFile().catch(console.error);
// [END storage_rename_file]
}
main(...process.argv.slice(2));
18 changes: 18 additions & 0 deletions samples/system-test/files.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const bucket = storage.bucket(bucketName);
const fileName = 'test.txt';
const movedFileName = 'test2.txt';
const copiedFileName = 'test3.txt';
const renamedFileName = 'test4.txt';
const signedFileName = 'signed-upload.txt';
const kmsKeyName = process.env.GOOGLE_CLOUD_KMS_KEY_US;
const filePath = path.join(cwd, 'resources', fileName);
Expand Down Expand Up @@ -178,6 +179,23 @@ describe('file', () => {
assert.notMatch(output, new RegExp(copiedFileName));
});

it('should rename a file', async () => {
const output = execSync(
`node renameFile.js ${bucketName} ${movedFileName} ${renamedFileName}`
);
assert.match(
output,
new RegExp(
`gs://${bucketName}/${movedFileName} renamed to gs://${bucketName}/${renamedFileName}.`
)
);
const [exists] = await bucket.file(renamedFileName).exists();
assert.strictEqual(exists, true);

const [oldFileExists] = await bucket.file(movedFileName).exists();
assert.strictEqual(oldFileExists, false);
});

it('should make a file public', () => {
const output = execSync(
`node makePublic.js ${bucketName} ${copiedFileName}`
Expand Down
112 changes: 112 additions & 0 deletions src/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ export interface MoveOptions {
userProject?: string;
}

export type RenameOptions = MoveOptions;
export type RenameResponse = MoveResponse;
export type RenameCallback = MoveCallback;

export type RotateEncryptionKeyOptions = string | Buffer | EncryptionKeyOptions;

export interface EncryptionKeyOptions {
Expand Down Expand Up @@ -3233,6 +3237,114 @@ class File extends ServiceObject<File> {
});
}

rename(
destinationFile: string | File,
options?: RenameOptions
): Promise<RenameResponse>;
rename(destinationFile: string | File, callback: RenameCallback): void;
rename(
destinationFile: string | File,
options: RenameOptions,
callback: RenameCallback
): void;
/**
* @typedef {array} RenameResponse
* @property {File} 0 The destination File.
* @property {object} 1 The full API response.
*/
/**
* @callback RenameCallback
* @param {?Error} err Request error, if any.
* @param {?File} destinationFile The destination File.
* @param {object} apiResponse The full API response.
*/
/**
* @typedef {object} RenameOptions Configuration options for File#move(). See an
* [Object
* resource](https://cloud.google.com/storage/docs/json_api/v1/objects#resource).
* @param {string} [userProject] The ID of the project which will be
* billed for the request.
*/
/**
* Rename this file.
*
* **Warning**:
* There is currently no atomic `rename` method in the Cloud Storage API,
* so this method is an alias of {@link File#move}, which in turn is a
* composition of {@link File#copy} (to the new location) and
* {@link File#delete} (from the old location). While
* unlikely, it is possible that an error returned to your callback could be
* triggered from either one of these API calls failing, which could leave a
* duplicate file lingering. The error message will indicate what operation
* has failed.
*
* @param {string|File} destinationFile Destination file.
* @param {RenameCallback} [callback] Callback function.
* @returns {Promise<RenameResponse>}
*
* @example
* const {Storage} = require('@google-cloud/storage');
* const storage = new Storage();
*
* //-
* // You can pass in a string or a File object.
* //
* // For all of the below examples, assume we are working with the following
* // Bucket and File objects.
* //-
*
* const bucket = storage.bucket('my-bucket');
* const file = bucket.file('my-image.png');
*
* //-
* // You can pass in a string for the destinationFile.
* //-
* file.rename('renamed-image.png', function(err, renamedFile, apiResponse) {
* // `my-bucket` no longer contains:
* // - "my-image.png"
* // but contains instead:
* // - "renamed-image.png"
*
* // `renamedFile` is an instance of a File object that refers to your
* // renamed file.
* });
*
* //-
* // You can pass in a File object.
* //-
* const anotherFile = anotherBucket.file('my-awesome-image.png');
*
* file.rename(anotherFile, function(err, renamedFile, apiResponse) {
* // `my-bucket` no longer contains:
* // - "my-image.png"
*
* // Note:
* // The `renamedFile` parameter is equal to `anotherFile`.
* });
*
* //-
* // If the callback is omitted, we'll return a Promise.
* //-
* file.rename('my-renamed-image.png').then(function(data) {
* const renamedFile = data[0];
* const apiResponse = data[1];
* });
*/
rename(
destinationFile: string | File,
optionsOrCallback?: RenameOptions | RenameCallback,
callback?: RenameCallback
): Promise<RenameResponse> | void {
const options =
typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
callback =
typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;

callback = callback || util.noop;

this.move(destinationFile, options, callback);
}

request(reqOpts: DecorateRequestOptions): Promise<[ResponseBody, Metadata]>;
request(
reqOpts: DecorateRequestOptions,
Expand Down
17 changes: 17 additions & 0 deletions system-test/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1950,6 +1950,23 @@ describe('storage', () => {
})
);

it(
'file#rename',
doubleTest((options: GetFileOptions, done: SaveCallback) => {
const newFile = bucketNonAllowList.file(generateName());

file.rename(newFile, options, err => {
if (err) {
done(err);
return;
}

// Re-create the file. The tests need it.
file.save('newcontent', options, done);
});
})
);

it(
'file#setMetadata',
doubleTest(
Expand Down
35 changes: 35 additions & 0 deletions test/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
SignedPostPolicyV4Output,
GenerateSignedPostPolicyV4Options,
STORAGE_POST_POLICY_BASE_URL,
MoveOptions,
} from '../src/file';

let promisified = false;
Expand Down Expand Up @@ -3679,6 +3680,40 @@ describe('File', () => {
});
});

describe('rename', () => {
it('should correctly call File#move', done => {
const newFileName = 'renamed-file.txt';
const options = {};
file.move = (dest: string, opts: MoveOptions, cb: Function) => {
assert.strictEqual(dest, newFileName);
assert.strictEqual(opts, options);
assert.strictEqual(cb, done);
cb();
};
file.rename(newFileName, options, done);
});

it('should accept File object', done => {
const newFileObject = new File(BUCKET, 'renamed-file.txt');
const options = {};
file.move = (dest: string, opts: MoveOptions, cb: Function) => {
assert.strictEqual(dest, newFileObject);
assert.strictEqual(opts, options);
assert.strictEqual(cb, done);
cb();
};
file.rename(newFileObject, options, done);
});

it('should not require options', done => {
file.move = (dest: string, opts: MoveOptions, cb: Function) => {
assert.deepStrictEqual(opts, {});
cb();
};
file.rename('new-name', done);
});
});

describe('request', () => {
it('should call the parent request function', () => {
const options = {};
Expand Down