diff --git a/storage/composeFile.js b/storage/composeFile.js new file mode 100644 index 0000000000..cf5a43c4b8 --- /dev/null +++ b/storage/composeFile.js @@ -0,0 +1,72 @@ +// Copyright 2024 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. + +'use strict'; + +/** + * This application demonstrates how to compose/combine multiple files into a + * single destination file 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', + firstFileName = 'first.txt', + secondFileName = 'second.txt', + destinationFileName = 'destination.txt', + deleteSourceObjects = false +) { + // [START storage_compose_file] + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const bucketName = 'your-unique-bucket-name'; + // const firstFileName = 'first.txt'; + // const secondFileName = 'second.txt'; + // const destinationFileName = 'destination.txt'; + // const deleteSourceObjects = false; + + // Imports the Google Cloud client library + const {Storage} = require('@google-cloud/storage'); + + // Creates a client + const storage = new Storage(); + + async function composeFile() { + const bucket = storage.bucket(bucketName); + const sources = [firstFileName, secondFileName]; + + await bucket.combine(sources, destinationFileName); + + console.log( + `New composite file ${destinationFileName} was created by combining ${sources.join(', ')}` + ); + + if (String(deleteSourceObjects) === 'true') { + await Promise.all( + sources.map(async source => { + await bucket.file(source).delete(); + }) + ); + console.log(`Deleted source objects: ${sources.join(', ')}`); + } + } + + composeFile().catch(console.error); + // [END storage_compose_file] +} + +main(...process.argv.slice(2)); diff --git a/storage/system-test/files.test.js b/storage/system-test/files.test.js index a0147fa8a0..c5105c65aa 100644 --- a/storage/system-test/files.test.js +++ b/storage/system-test/files.test.js @@ -146,6 +146,57 @@ describe('file', () => { await bucket.file(noContextFileName).delete(); }); }); + + describe('compose file', () => { + it('should combine multiple files into one new file and optionally delete source objects', async () => { + const firstFileName = 'first.txt'; + const secondFileName = 'second.txt'; + const destinationFileName = 'destination.txt'; + + // upload test.txt twice + await bucket.upload(filePath, {destination: firstFileName}); + await bucket.upload(filePath, {destination: secondFileName}); + + // Test with deleteSourceObjects = false + let output = execSync( + `node composeFile.js ${bucketName} ${firstFileName} ${secondFileName} ${destinationFileName} false` + ); + assert.include( + output, + `New composite file ${destinationFileName} was created by combining ${firstFileName}, ${secondFileName}` + ); + assert.notInclude(output, 'Deleted source objects'); + + let [destExists] = await bucket.file(destinationFileName).exists(); + assert.strictEqual(destExists, true); + let [firstExists] = await bucket.file(firstFileName).exists(); + assert.strictEqual(firstExists, true); + let [secondExists] = await bucket.file(secondFileName).exists(); + assert.strictEqual(secondExists, true); + + await bucket.file(destinationFileName).delete(); + + // Test with deleteSourceObjects = true + output = execSync( + `node composeFile.js ${bucketName} ${firstFileName} ${secondFileName} ${destinationFileName} true` + ); + assert.include( + output, + `New composite file ${destinationFileName} was created by combining ${firstFileName}, ${secondFileName}` + ); + assert.include( + output, + `Deleted source objects: ${firstFileName}, ${secondFileName}` + ); + + [destExists] = await bucket.file(destinationFileName).exists(); + assert.strictEqual(destExists, true); + [firstExists] = await bucket.file(firstFileName).exists(); + assert.strictEqual(firstExists, false); + [secondExists] = await bucket.file(secondFileName).exists(); + assert.strictEqual(secondExists, false); + }); + }); }); function generateName() {