Skip to content

Commit b0f32ce

Browse files
feat: add ability to upload entire directory, add samples for upload … (#2118)
* feat: add ability to upload entire directory, add samples for upload directory / download folder * linter fix * update phrasing of comments * update comment in downloadFolderWithTransferManager sample * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 7d93fff commit b0f32ce

9 files changed

+218
-8
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-storage/tre
133133
| Download File | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadFile.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadFile.js,samples/README.md) |
134134
| Download a File in Chunks Utilzing Transfer Manager | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadFileInChunksWithTransferManager.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadFileInChunksWithTransferManager.js,samples/README.md) |
135135
| Download File Using Requester Pays | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadFileUsingRequesterPays.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadFileUsingRequesterPays.js,samples/README.md) |
136+
| Download Folder With Transfer Manager | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadFolderWithTransferManager.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadFolderWithTransferManager.js,samples/README.md) |
136137
| Download Into Memory | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadIntoMemory.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadIntoMemory.js,samples/README.md) |
137138
| Download Many Files With Transfer Manager | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadManyFilesWithTransferManager.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadManyFilesWithTransferManager.js,samples/README.md) |
138139
| Storage Download Public File. | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadPublicFile.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadPublicFile.js,samples/README.md) |
@@ -205,6 +206,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/nodejs-storage/tre
205206
| Stream File Download | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/streamFileDownload.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/streamFileDownload.js,samples/README.md) |
206207
| Stream File Upload | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/streamFileUpload.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/streamFileUpload.js,samples/README.md) |
207208
| Upload a directory to a bucket. | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/uploadDirectory.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/uploadDirectory.js,samples/README.md) |
209+
| Upload Directory With Transfer Manager | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/uploadDirectoryWithTransferManager.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/uploadDirectoryWithTransferManager.js,samples/README.md) |
208210
| Upload Encrypted File | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/uploadEncryptedFile.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/uploadEncryptedFile.js,samples/README.md) |
209211
| Upload File | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/uploadFile.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/uploadFile.js,samples/README.md) |
210212
| Upload File With Kms Key | [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/uploadFileWithKmsKey.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/uploadFileWithKmsKey.js,samples/README.md) |

samples/README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ objects to users via direct download.
5252
* [Download File](#download-file)
5353
* [Download a File in Chunks Utilzing Transfer Manager](#download-a-file-in-chunks-utilzing-transfer-manager)
5454
* [Download File Using Requester Pays](#download-file-using-requester-pays)
55+
* [Download Folder With Transfer Manager](#download-folder-with-transfer-manager)
5556
* [Download Into Memory](#download-into-memory)
5657
* [Download Many Files With Transfer Manager](#download-many-files-with-transfer-manager)
5758
* [Storage Download Public File.](#storage-download-public-file.)
@@ -124,6 +125,7 @@ objects to users via direct download.
124125
* [Stream File Download](#stream-file-download)
125126
* [Stream File Upload](#stream-file-upload)
126127
* [Upload a directory to a bucket.](#upload-a-directory-to-a-bucket.)
128+
* [Upload Directory With Transfer Manager](#upload-directory-with-transfer-manager)
127129
* [Upload Encrypted File](#upload-encrypted-file)
128130
* [Upload File](#upload-file)
129131
* [Upload File With Kms Key](#upload-file-with-kms-key)
@@ -752,6 +754,25 @@ __Usage:__
752754

753755

754756

757+
### Download Folder With Transfer Manager
758+
759+
Downloads a folder in parallel utilizing transfer manager.
760+
761+
View the [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadFolderWithTransferManager.js).
762+
763+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/downloadFolderWithTransferManager.js,samples/README.md)
764+
765+
__Usage:__
766+
767+
768+
`node downloadFolderWithTransferManager.js <BUCKET_NAME> <FOLDER_NAME>`
769+
770+
771+
-----
772+
773+
774+
775+
755776
### Download Into Memory
756777

757778
View the [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/downloadIntoMemory.js).
@@ -2012,6 +2033,25 @@ __Usage:__
20122033

20132034

20142035

2036+
### Upload Directory With Transfer Manager
2037+
2038+
Uploads a directory in parallel utilizing transfer manager.
2039+
2040+
View the [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/uploadDirectoryWithTransferManager.js).
2041+
2042+
[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/nodejs-storage&page=editor&open_in_editor=samples/uploadDirectoryWithTransferManager.js,samples/README.md)
2043+
2044+
__Usage:__
2045+
2046+
2047+
`node uploadFolderWithTransferManager.js <BUCKET_NAME> <DIRECTORY_NAME>`
2048+
2049+
2050+
-----
2051+
2052+
2053+
2054+
20152055
### Upload Encrypted File
20162056

20172057
View the [source code](https://github.com/googleapis/nodejs-storage/blob/main/samples/uploadEncryptedFile.js).

samples/downloadFileInChunksWithTransferManager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function main(
5151
// Creates a client
5252
const storage = new Storage();
5353

54-
// Creates a transfer manager instance
54+
// Creates a transfer manager client
5555
const transferManager = new TransferManager(storage.bucket(bucketName));
5656

5757
async function downloadFileInChunksWithTransferManager() {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright 2022 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.
15+
* @experimental
16+
*/
17+
18+
// sample-metadata:
19+
// title: Download Folder With Transfer Manager
20+
// description: Downloads a folder in parallel utilizing transfer manager.
21+
// usage: node downloadFolderWithTransferManager.js <BUCKET_NAME> <FOLDER_NAME>
22+
23+
function main(bucketName = 'my-bucket', folderName = 'my-folder') {
24+
// [START storage_download_folder_transfer_manager]
25+
/**
26+
* TODO(developer): Uncomment the following lines before running the sample.
27+
*/
28+
// The ID of your GCS bucket
29+
// const bucketName = 'your-unique-bucket-name';
30+
31+
// The ID of the GCS folder to download. The folder will be downloaded to the local path of the executing code.
32+
// const folderName = 'your-folder-name';
33+
34+
// Imports the Google Cloud client library
35+
const {Storage, TransferManager} = require('@google-cloud/storage');
36+
37+
// Creates a client
38+
const storage = new Storage();
39+
40+
// Creates a transfer manager client
41+
const transferManager = new TransferManager(storage.bucket(bucketName));
42+
43+
async function downloadFolderWithTransferManager() {
44+
// Downloads the folder
45+
await transferManager.downloadManyFiles(folderName);
46+
47+
console.log(
48+
`gs://${bucketName}/${folderName} downloaded to ${folderName}.`
49+
);
50+
}
51+
52+
downloadFolderWithTransferManager().catch(console.error);
53+
// [END storage_download_folder_transfer_manager]
54+
}
55+
56+
process.on('unhandledRejection', err => {
57+
console.error(err.message);
58+
process.exitCode = 1;
59+
});
60+
main(...process.argv.slice(2));

samples/downloadManyFilesWithTransferManager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function main(
4444
// Creates a client
4545
const storage = new Storage();
4646

47-
// Creates a transfer manager instance
47+
// Creates a transfer manager client
4848
const transferManager = new TransferManager(storage.bucket(bucketName));
4949

5050
async function downloadManyFilesWithTransferManager() {

samples/system-test/transfer-manager.test.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@ const bucketName = generateName();
2828
const bucket = storage.bucket(bucketName);
2929
const firstFileName = 'test.txt';
3030
const secondFileName = 'test2.txt';
31-
const firstFilePath = path.join(cwd, 'resources', firstFileName);
32-
const secondFilePath = path.join(cwd, 'resources', secondFileName);
31+
const resourcesPath = path.join(cwd, 'resources');
32+
const firstFilePath = path.join(resourcesPath, firstFileName);
33+
const secondFilePath = path.join(resourcesPath, secondFileName);
3334
const downloadFilePath = path.join(cwd, 'downloaded.txt');
3435
const chunkSize = 1024;
3536

@@ -78,6 +79,28 @@ describe('transfer manager', () => {
7879
)
7980
);
8081
});
82+
83+
it('should upload a directory', async () => {
84+
const output = execSync(
85+
`node uploadDirectoryWithTransferManager.js ${bucketName} ${resourcesPath}`
86+
);
87+
assert.match(
88+
output,
89+
new RegExp(`${resourcesPath} uploaded to ${bucketName}.`)
90+
);
91+
});
92+
93+
it('should download a directory', async () => {
94+
const output = execSync(
95+
`node downloadFolderWithTransferManager.js ${bucketName} ${resourcesPath}`
96+
);
97+
assert.match(
98+
output,
99+
new RegExp(
100+
`gs://${bucketName}/${resourcesPath} downloaded to ${resourcesPath}.`
101+
)
102+
);
103+
});
81104
});
82105

83106
function generateName() {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
* Copyright 2022 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.
15+
* @experimental
16+
*/
17+
18+
// sample-metadata:
19+
// title: Upload Directory With Transfer Manager
20+
// description: Uploads a directory in parallel utilizing transfer manager.
21+
// usage: node uploadFolderWithTransferManager.js <BUCKET_NAME> <DIRECTORY_NAME>
22+
23+
function main(bucketName = 'my-bucket', directoryName = 'my-directory') {
24+
// [START storage_upload_directory_transfer_manager]
25+
/**
26+
* TODO(developer): Uncomment the following lines before running the sample.
27+
*/
28+
// The ID of your GCS bucket
29+
// const bucketName = 'your-unique-bucket-name';
30+
31+
// The local directory to upload
32+
// const directoryName = 'your-directory';
33+
34+
// Imports the Google Cloud client library
35+
const {Storage, TransferManager} = require('@google-cloud/storage');
36+
37+
// Creates a client
38+
const storage = new Storage();
39+
40+
// Creates a transfer manager client
41+
const transferManager = new TransferManager(storage.bucket(bucketName));
42+
43+
async function uploadDirectoryWithTransferManager() {
44+
// Uploads the directory
45+
await transferManager.uploadManyFiles(directoryName);
46+
47+
console.log(`${directoryName} uploaded to ${bucketName}.`);
48+
}
49+
50+
uploadDirectoryWithTransferManager().catch(console.error);
51+
// [END storage_upload_directory_transfer_manager]
52+
}
53+
54+
process.on('unhandledRejection', err => {
55+
console.error(err.message);
56+
process.exitCode = 1;
57+
});
58+
main(...process.argv.slice(2));

samples/uploadManyFilesWithTransferManager.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ function main(
4444
// Creates a client
4545
const storage = new Storage();
4646

47-
// Creates a transfer manager instance
47+
// Creates a transfer manager client
4848
const transferManager = new TransferManager(storage.bucket(bucketName));
4949

5050
async function uploadManyFilesWithTransferManager() {

src/transfer-manager.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ export class TransferManager {
9999
* Upload multiple files in parallel to the bucket. This is a convenience method
100100
* that utilizes {@link Bucket#upload} to perform the upload.
101101
*
102-
* @param {array} [filePaths] An array of fully qualified paths to the files.
102+
* @param {array | string} [filePathsOrDirectory] An array of fully qualified paths to the files or a directory name.
103+
* If a directory name is provided, the directory will be recursively walked and all files will be added to the upload list.
103104
* to be uploaded to the bucket
104105
* @param {UploadManyFilesOptions} [options] Configuration options.
105106
* @returns {Promise<UploadResponse[]>}
@@ -118,11 +119,13 @@ export class TransferManager {
118119
* // Your bucket now contains:
119120
* // - "local/path/file1.txt" (with the contents of '/local/path/file1.txt')
120121
* // - "local/path/file2.txt" (with the contents of '/local/path/file2.txt')
122+
* const response = await transferManager.uploadManyFiles('/local/directory');
123+
* // Your bucket will now contain all files contained in '/local/directory' maintaining the subdirectory structure.
121124
* ```
122125
* @experimental
123126
*/
124127
async uploadManyFiles(
125-
filePaths: string[],
128+
filePathsOrDirectory: string[] | string,
126129
options: UploadManyFilesOptions = {}
127130
): Promise<UploadResponse[]> {
128131
if (options.skipIfExists && options.passthroughOptions?.preconditionOpts) {
@@ -142,8 +145,18 @@ export class TransferManager {
142145
options.concurrencyLimit || DEFAULT_PARALLEL_UPLOAD_LIMIT
143146
);
144147
const promises = [];
148+
let allPaths: string[] = [];
149+
if (!Array.isArray(filePathsOrDirectory)) {
150+
for await (const curPath of this.getPathsFromDirectory(
151+
filePathsOrDirectory
152+
)) {
153+
allPaths.push(curPath);
154+
}
155+
} else {
156+
allPaths = filePathsOrDirectory;
157+
}
145158

146-
for (const filePath of filePaths) {
159+
for (const filePath of allPaths) {
147160
const stat = await fsp.lstat(filePath);
148161
if (stat.isDirectory()) {
149162
continue;
@@ -355,4 +368,18 @@ export class TransferManager {
355368
});
356369
});
357370
}
371+
372+
private async *getPathsFromDirectory(
373+
directory: string
374+
): AsyncGenerator<string> {
375+
const filesAndSubdirectories = await fsp.readdir(directory, {
376+
withFileTypes: true,
377+
});
378+
for (const curFileOrDirectory of filesAndSubdirectories) {
379+
const fullPath = path.join(directory, curFileOrDirectory.name);
380+
curFileOrDirectory.isDirectory()
381+
? yield* this.getPathsFromDirectory(fullPath)
382+
: yield fullPath;
383+
}
384+
}
358385
}

0 commit comments

Comments
 (0)