Exercise - Work with the file system
Tailwind Traders has many physical stores all over the world. Each night, these stores create a file called sales.json that contains the total for all their sales for the previous day. These files are organized in folders named by store ID.
In this exercise, you write a Node.js program that can search for files called sales.json in a folder.
Open project in development container
Start the process to create a new GitHub Codespace on the
main
branch of theMicrosoftDocs/node-essentials
GitHub repository.On the Create codespace page, review the codespace configuration settings and then select Create new codespace
Wait for the codespace to start. This startup process can take a few minutes.
Open a new terminal in the codespace.
Validate that Node.js is installed in your environment:
node --version
The dev container uses a Node.js LTS version such as
v20.5.1
. The exact version might be different.The remaining exercises in this project take place in the context of this development container.
Find the sales.json files
Your task is to find all the files in the stores folder.
Expand the stores folder and each of the numbered folders inside.
Include the fs module
In the
./nodejs-files
subfolder, create a index.js file to open it in the editor.At the top of the file, add the following code to include the fs module in the file.
const fs = require("fs").promises;
Next, create the
main
function which is the entry point for your code. The last line of code in this file invokes themain
method.const fs = require("fs").promises; async function main() {} main();
This is typical CommonJS boilerplate code to call an asynchronous function.
Write a function to find the sales.json
files
Create a new function called
findSalesFiles
that takes afolderName
parameter.async function findSalesFiles(folderName) { // FIND SALES FILES }
Inside the
findSalesFiles
function, add the following code to complete these tasks:- (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
- (2) Read the currentFolder with the
readdir
method. - (3) Add a block to loop over each item returned from the
readdir
method using the asynchronousfor...of
loop. - (4) Add an
if
statement to determine if the item is a file or a directory. - (5) If the item is a directory, recursively call the function
findSalesFiles
again, passing in the path to the item. - (6) If it's not a directory, add a check to make sure the item name matches sales.json.
async function findSalesFiles(folderName) { // (1) Add an array at the top, to hold the paths to all the sales files that the program finds. let results = []; // (2) Read the currentFolder with the `readdir` method. const items = await fs.readdir(folderName, { withFileTypes: true }); // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. for (const item of items) { // (4) Add an `if` statement to determine if the item is a file or a directory. if (item.isDirectory()) { // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`); results = results.concat(resultsReturned); } else { // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*. if (item.name === "sales.json") { results.push(`${folderName}/${item.name}`); } } } return results; }
Call this new
findSaleFiles
function from themain
method. Pass in the stores folder name as the location to search for files.async function main() { const results = await findSalesFiles("stores"); console.log(results); }
The full application looks like:
const fs = require("fs").promises; async function findSalesFiles(folderName) { // (1) Add an array at the top, to hold the paths to all the sales files that the program finds. let results = []; // (2) Read the currentFolder with the `readdir` method. const items = await fs.readdir(folderName, { withFileTypes: true }); // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. for (const item of items) { // (4) Add an `if` statement to determine if the item is a file or a directory. if (item.isDirectory()) { // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`); results = results.concat(resultsReturned); } else { // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*. if (item.name === "sales.json") { results.push(`${folderName}/${item.name}`); } } } return results; } async function main() { const results = await findSalesFiles("stores"); console.log(results); } main();
Run the program
Enter the following command at the terminal to run the program.
node index.js
The program should show the following output.
[ 'stores/201/sales.json', 'stores/202/sales.json', 'stores/203/sales.json', 'stores/204/sales.json', ]
Excellent! You've successfully written a command-line program that can traverse any directory and find all the sales.json files inside.
However, the way that the path to subfolders was constructed in this example is a little clumsy because it requires concatenating strings together. Also, you might run into issues on other operating systems (like Windows) that use different path separators.
In the next section, you'll learn how to construct paths that work across operating systems by using the path module.
Got stuck?
If you got stuck at any point in this exercise, here's the completed code. Remove everything in index.js and replace it with this solution.
const fs = require("fs").promises;
async function findSalesFiles(folderName) {
// (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
let results = [];
// (2) Read the currentFolder with the `readdir` method.
const items = await fs.readdir(folderName, { withFileTypes: true });
// (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop.
for (const item of items) {
// (4) Add an `if` statement to determine if the item is a file or a directory.
if (item.isDirectory()) {
// (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item.
const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`);
results = results.concat(resultsReturned);
} else {
// (6) If it's not a directory, add a check to make sure the item name matches *sales.json*.
if (item.name === "sales.json") {
results.push(`${folderName}/${item.name}`);
}
}
}
return results;
}
async function main() {
const results = await findSalesFiles("stores");
console.log(results);
}
main();