Do not read directories after 3 level using node js - javascript

Is there any way to stop reading directory after specific point or 3 level tree structure
for example
MainFolder
-ABC Folder
-DEF Folder
-GHI Folder
-image.png
-jkl.gif
when i click on ABC folder my path will look like this /MainFolder/ABC Folder
if there is any other folder or file in it then it will look like
/MainFolder/ABC Folder
-PQR Folder
-STU Folder
-abc.pdf
-xyz.txt
click on PQR folder then it will look like this
/MainFolder/ABC Folder/PQR Folder
-DFC Folder
-HKJ Folder
-mnb.pdf
-xyz.txt
but it should not read DFC Folder/HKJ Folder or any other folder which are present after 3level tree structure
Output :-
["MainFolder/image.png",
"MainFolder/jkl.gif",
"MainFolder/ABC Folder/abc.pdf",
"MainFolder/ABC Folder/xyz.txt",
"MainFolder/ABC Folder/PQR Folder/mnb.pdf",
"MainFolder/ABC Folder/PQR Folder/xyz.txt"]
code which i have it read all files and sub directories but i want to stop at 3level directory
async function getAllFile(folderPath) {
let files = await fs.readdir(folderPath);
files = await Promise.all(
files.map(async (file) => {
const filePath = path.join(folderPath, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
return getAllFile(filePath);
} else if (stats.isFile()) return filePath;
})
);
return files.reduce((all, folderContents) => all.concat(folderContents), []);
}
PS : using node 10.16.3

This should solve your problem. I added the depth parameter, which basically stands for how many folder levels you will want to traverse. In the case of your file tree, you would call this function with depth 2: getAllFile('./MainFolder/', 2) as you want to explore the root (level 1) and the subfolders (level 2), but not the folders within the subfolders (level 3).
I also return null if a folder remains unexplored, as this would otherwise result in undefined values. Before returning I filter out these null values.
async function getAllFile(folderPath, depth) {
depth -= 1;
let files = await fs.readdir(folderPath);
files = await Promise.all(
files.map(async (file) => {
const filePath = path.join(folderPath, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory() && depth > 0) {
return getAllFile(filePath, depth);
} else if (stats.isFile()) return filePath;
else return null;
})
);
return files.reduce((all, folderContents) => all.concat(folderContents), []).filter(e => e != null);
}

Related

How to delete multiple PDF files in the current directory in javascript/node.js

I wanted to delete multiple files which are ending with .pdf under the current directory. Suppose I have 3 different pdf files, 1 image file, and one text file, so in these, I want to delete those 3 different pdf files only.
What I have tried.
1st method
fs.unlinkSync('./'+*+pdfname); -> I know this does not make sense
2nd method
try {
var files = (here the list of files should come. However i am failing to get those);
var path="./"
files.forEach(path => fs.existsSync(path) && fs.unlinkSync(path))
} catch (err) {
console.error("not exist")
}
Any different approaches would be appreciated.
Update for the solution:
I have got the solution for my requirement, I just wanted my function to delete all the pdf files and function to be synchronous. However 99% of the solution given by the below author -> https://stackoverflow.com/a/66558251/11781464
fs.readdir is asynchronous and just needs to make it synchronous fs.readdirSync.
below is the updated code and all the credit should go to the author https://stackoverflow.com/a/66558251/11781464.
Updated code
try {
const path = './'
// Read the directory given in `path`
fs.readdirSync(path).forEach((file) => {
// Check if the file is with a PDF extension, remove it
if (file.split('.').pop().toLowerCase() === 'pdf') {
console.log(`Deleting file: ${file}`);
fs.unlinkSync(path + file)
}
});
console.log("Deleted all the pdf files")
return true;
} catch (err) {
console.error("Error in deleting files",err);
}
You can read the directory using fs.readdir and then check for PDF files and delete them. Like this:
fs = require('fs');
try {
path = './'
// Read the directory given in `path`
const files = fs.readdir(path, (err, files) => {
if (err)
throw err;
files.forEach((file) => {
// Check if the file is with a PDF extension, remove it
if (file.split('.').pop().toLowerCase() == 'pdf') {
console.log(`Deleting file: ${file}`);
fs.unlinkSync(path + file)
}
});
});
} catch (err) {
console.error(err);
}
Preliminary Reading
The current working directory of the Node.js process - see update below answer
Path methods
resolve
extName
File System methods
fs.readdirSync
fs.unlinkSync
and classes
dirent
Example
"use strict";
const fs = require('fs');
const path = require('path');
const cwd = process.cwd();
fs.readdirSync( cwd, {withFileTypes: true})
.forEach( dirent => {
if(dirent.isFile()) {
const fileName = dirent.name;
if( path.extname(fileName).toLowerCase() === ".pdf") {
fs.unlinkSync( path.resolve( cwd, fileName));
}
}
});
Notes
untested code
If unlinkSync fails I would assume it returns -1 as per the unlink(2) man page linked in documentation. Personally I would test this using a filename that doesn't exist in cwd.
I believe the {withFileTypes: true} option for readdirSync returns dirent objects with a mime-type value that would allow you to check for files of type application/pdf regardless of extension (not attempted in the example).
Update: path(resolve) adds the current working directory at the beginning of the returned path, when necessary, by default. path.resolve(fileName) will work equally as well as path.resolve(cwd, fileName) in the example.
It seems you know how to delete (unlink) the files - you are asking how to get the file paths?
Try using glob:
const pdfFiles = require("glob").globSync("*.pdf");
Hi here i am attaching tested code for delete all ( only ) .pdf files and not other extension files like .txt , .docs etc from directory.
Note : You can delete any files or directory only from server side.
const fs = require('fs');
const path = require('path')
fs.readdir('../path to directory', (err, files) => {
const pdfFiles = files.filter(el => path.extname(el) === '.pdf')
pdfFiles.forEach(file => {
console.log("Removing File -> ",file);
var filename = "../path to directory/"+file;
fs.unlink(filename,function(err){
if(err) return console.log(err);
console.log('file deleted successfully');
});
});
});
This will gives you a following result in console log.
Removing File -> note.pdf
Removing File -> note2.pdf
file deleted successfully
file deleted successfully
Please feel free to comment any query if have..

Dir walking to get abs path array of various files

Goal: Get a List of Absolute Paths for all files in a directory recursively leveraging NodeJs.
Info: As a python dev, I normally use the python packages which handle this in a platform independent fashion. My boss wanted some javascript code which would handle this goal... and as a JS dev previously, I was like "oh this is easy. Let's look up the node as I never got a chance to get my hands dirty with it." but I seem to be mistaken.
I don't see anything in node relating to Dir Walking, or a way I could hack together to create such a thing.
I was looking in "Child Process", "Console", "File System", "OS", "Path", and "Process". I didn't see anything which would do anything akin to:
pushd .
cd $dir
for folder in $(ls);
do
pushd .
cd $folder
//call again
ls $(pwd)$flag >> $dir/files_matching.txt
popd
done;
// or any platform independent means of recursively getting
// all files and their abs path which meet flag criterion,
// such as "*.txt" || "_*found*"
I could use child process to carry out Command Line items, but then I need to create a bunch of conditionals based on the OS consuming the app, and figured this would be something which already exists.
I don't want to reinvent the wheel, but figured this has already been done; I just don't see it in the base modules.
Is there a node module I would need which accomplishes this, which is outside of the base modules?
I am trying not to have to hand roll a conditional os based system to get an exhaustive list of abs paths for all files under a directory (or subset due to extensions, etc.)
I'd do it like this:
synchronous:
const fs = require("fs");
const { resolve } = require("path");
const getFiles = dir => {
const stack = [resolve(dir)];
const files = [];
while (stack.length) {
dir = stack.pop();
fs.readdirSync(dir).forEach(item => {
const path = resolve(dir, item);
(fs.statSync(path).isDirectory() ? stack : files).push(path);
});
}
return files;
};
console.log(getFiles("."));
asynchronous:
const fs = require("fs");
const { resolve } = require("path");
const pify = require("util").promisify;
const readdir = pify(fs.readdir);
const stat = pify(fs.stat);
const getFiles = async dir => {
const files = await readdir(resolve(dir));
const filesP = files.map(async file => {
const path = resolve(dir, file);
return (await stat(path)).isDirectory() ? getFiles(path) : path;
});
// return (await Promise.all(filesP)).flat(); // flat supported in node ~11
return [].concat(...(await Promise.all(filesP)));
};
getFiles(".").then(console.log);
async demo https://repl.it/#marzelin/getFiles
So, I was looking at the filesystem module and noticed the function readDir
https://nodejs.org/dist/latest-v8.x/docs/api/fs.html#fs_fs_readdir_path_options_callback
which does the trick in part. I guess it wasnt named a method i would have looking for. I was looking for things involving LIST and DIR, but not READ.
Anyways, here is a way to read Dir.
var fs = require('fs');
if (process.argv.length <= 2) {
console.log("Usage: " + __filename + " path/to/directory");
process.exit(-1);
}
var path = process.argv[2];
fs.readdir(path, function(err, items) {
console.log(items);
for (var i=0; i<items.length; i++) {
console.log(items[i]);
}
});
You notice that this one above is Async, but there is a Sync variant, just add "Sync" to the signature. Now you need to determine if something is a directory:
let file = fs.statSync("path/to/directory")
let isDir = file.isDirectory()
So you can couple this all together.
var fs = require('fs')
function recurse_file_system(path, contains) {
let files = fs.readdirSync(path);
let dArr = [];
let fArr = [];
for (let i in files){
let newPath = path + "/" + files[i]
if (fs.statSync(newPath).isDirectory()){
dArr.push(newPath)
}else{
if (filter(files[i], ".txt")){
fArr.push(newPath)
}
}
}
if (arr.length == 0){
return fArr;
}else{
for (let d in dArr){
let rslt = recurse_file_system(dArr[d]);
for (let i in rslt){
fArr.push(rslt[i])
}
}
return fArr;
}
}
console.log("Files:")
console.log(recurse_file_system("/"))
Now if you want to extend this, all you need to do is add a filter to say, limit the size of returns based on particular criterion, such as file name limitation.
function filter(filename, contains){
let reg = new RegEx(contains)
return reg.test(filename)
}
and you can add it to the base case, where you see filter... OR you can just return the WHOLE set and filter it there with the List method, filter.

How do you properly check if filename exist and generate a suffix before writing it to the directory?

I have a function that writes a file to a directory:
response.pipe(fs.createWriteStream(fullPath))
But before that I want to check if the path already exist, and if so, add a suffix, e.g. file_1.txt (and if that exist, create file_2.txt instead...etc):
// Check if the path already exist
let fullPath = "C:/test/file.txt"
let dir = "C:/test/"
let fileName = "file"
let fileExt = ".txt"
if (fs.existsSync(fullPath)) {
// I tried using a while loop but I end up making it too complicated
...
}
// Write file to disk
response.pipe(fs.createWriteStream(destinationPath))
Question
How do I properly / efficiently do that?
The while loop is the correct way.
// Check if the path already exist
let fullPath = "C:/test/file.txt"
let dir = "C:/test/"
let fileName = "file"
let fileExt = ".txt"
let num = 0;
while (fs.existsSync(fullPath)) {
fullPath = `${dir}${fileName}_${num++}${fileExt}`;
}
After this, fullPath contains the first nonexistent file.
Note that there's a potential race condition. Some other process could create the file after your loop finishes.
If all of your file names are named the same thing + "_#".txt, I think the most (one of the most) efficient ways to check that would be something along the lines of:
Get all files from the directory
var files = [];
fs.readdir(dir, (err, files) => {
files.forEach(file => {
files.push(file);
});
})
You would then sort the array (could be expensive if a lot of files)... then last record would be the highest number which you can easily extract.
Another thing you could do is find the file which has the latest creation date using similar approach using the Stats class from FS.
Get all files from the directory and sort them:
var files = fs.readdirSync(pathName)
.map(function(v) {
return { name:v};
})
.sort(function(a, b) { return a.nam > b.name; })
.map(function(v) { return v.name; });
And by latest creation date.
var files = fs.readdirSync(dir)
.map(function(v) {
return { name:v,
time:fs.statSync(dir + v).mtime.getTime()
};
})
.sort(function(a, b) { return a.time - b.time; })
.map(function(v) { return v.name; });

recursively find the closest package.json node.js

I am newly with node js, and I would like to find recursively the closest package.json. Actually, continue finding package.json until will not hit it.
My folder tree
root/
-contarats/
-proto/
some.proto
-package.json
"script": {
"contracts": "generate-some-contracts contracts/proto contracts",
}
const input = process.argv[2]
const settings = require(path.resolve(input, 'package.json'))
Are you looking for a way to iterate through directories? If so heres a synchronous function that would do that
function search_sync(dir) {
var results = []
var list = fs.readdirSync(dir)
list.forEach(function(file) {
file = path.resolve(dir, file)
filename = file.split('\\');
filename = filename[filename.length-1]
var stat = fs.statSync(file)
if (stat && stat.isDirectory()) results = results.concat(search_sync(file))
else if(filename == 'package.json')results.push(file)
})
return results
}
That will return an array of any files that are named package.json with their full file path. EG:
search_sync('./')
[C:\Users\User\MyNodeProject\package.json,
C:\Users\User\MyNodeProject\npm\someDependency\package.json,
C:\Users\User\MyNodeProject\npm\someOtherDependency\package.json]
Personally, I'd then break each line by their '\' character and see which one is closer to my root folder
Looking at your tree of directories, the package.json file is not in contracts/proto, but in contracts. (I assume that contaracts is a typo.) Changing the first argument on the command line should help:
generate-some-contracts contracts contracts
Nevertheless, you ask about the recursive search for the nearest package.json. NPM does it, when looking for the package root. It starts in the current directory and then follows the ancestors, until it finds a package.json. A function reading and parsing that package.json, similarly to require, could look like this:
const { readFile } = require('fs/promises')
const { join, resolve } = require('path')
async function loadPackageJson(cwd) {
const startDir = cwd || process.env.INIT_CWD || process.cwd()
let dir = startDir, prevDir
do {
try {
const path = join(dir, 'package.json')
const content = await readFile(path, 'utf8')
return JSON.parse(content)
} catch (err) {
if (err.code !== 'ENOENT') throw err
}
prevDir = dir
dir = resolve(dir, '..')
} while (prevDir !== dir)
throw new Error(`package.json not found in ${startDir} and its ancestors`)
}

get one file from each directory

in my app user can have multiple directory in setting , I want to get one file for each folder, note these directories can have their own directories an nested as possible
my config.json
{"dirs":["F:/music/Ellie Goulding","F:/music/Eminem - Revival [Album] [iTunes Version] - [7tunes]"]}
var findSongs = async function (baseDir,isDir=false,musics) {
try{
musics =musics || []
var files = await getDirFiles(baseDir)
for(let i =0;i<files.length;i++){
try {
let file = files[i]
var musicPath = `${baseDir}/${file}`
let stat = fs.lstatSync(musicPath)
if(stat.isDirectory()){
await findSongs(musicPath,true,musics)
}else{
let meta = await getMusicMeta(musicPath)
if(meta){
musics.push(meta)
}
if(isDir){
break
}
}
issue with this code is it goes get all songs of second folder and doesn't return anything from folder one but if only place one folder like this : F:/music it will work as expected

Categories

Resources