get one file from each directory - javascript

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

Related

Get the file path where user downloaded a file

I currently have a function that prompts the user to download a JSON file:
function downloadObjectAsJson (exportObj, exportName) {
const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(exportObj))
let downloadAnchorNode = document.createElement('a')
downloadAnchorNode.setAttribute('href', dataStr)
downloadAnchorNode.setAttribute('download', exportName + '.json')
document.body.appendChild(downloadAnchorNode) // required for firefox
downloadAnchorNode.click()
downloadAnchorNode.remove()
}
Is there a way to get the path the user selected to download this file to? Just need it to be displayed on the UI.
There are some API available that somehow allows access to the client file system like this, which lists down all the files in the selected directory:
async function listFilesInDirectory () {
const dirHandle = await window.showDirectoryPicker()
const promises = []
for await (const entry of dirHandle.values()) {
if (entry.kind !== 'file') {
break
}
promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`))
}
console.log(await Promise.all(promises))
}
So I thought there might be some way to also get the path selected by the user when saving files.
Any other suggestions/means are welcome.

Do not read directories after 3 level using node js

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);
}

Get desktop file icons using NodeJs

I am trying to create a desktop launcher application in Electron that reads the number of files in the user's desktop and gathers the information from each file found. I am gathering the files and constructing the path based on the directory, but all I can get is the filenames. I am not sure how to store the file itself and extract the desktop icon from it. I haven't seen many solutions to it, other than using the AxtiveXobject however supposedly certain implementations do not work in the latest nodejs. Here is my code so far.
//requiring path and fs modules
const path = require('path');
const fs = require('fs');
//gets home directory
const homedir = require('os').homedir();
//specifies to desktop
const dir = `${homedir}/Desktop`;
var walk = require('walk');
var filepaths = [];
//storing desktop path
var desktopDir = dir;
console.log(desktopDir);
//gets the desktop files and paths
function getDesktopFiles(_dir){
//read directory
fs.readdir(_dir, (err, files) => {
if (err)
console.log(err);
else {
files.forEach(_file => {
//console.log(_file);
let _p = _dir + '/'+_file;
//changes slashing for file paths
let _path = _p.replace(/\\/g, "/");
filepaths.push(_path);
})
}
})
for(let p of filepaths){
console.log(p);
}
}
getDesktopFiles(desktopDir);
Here is a quick snippet of code which works for me in an Electron renderer process; it has been successfully tested on both macOS and Linux, and should be platform-independent.
It lists all the files located on the user's desktop and displays each file's icon and name at the end of the HTML page; it makes use of the following Electron API functions:
app.getPath
app.getFileIcon
image.toDataURL
image.getSize
const { app, nativeImage } = require ('electron').remote;
const path = require ('path');
const fs = require ('fs');
//
const desktopPath = app.getPath ('desktop');
let filePaths = fs.readdirSync (desktopPath);
for (let filePath of filePaths)
{
app.getFileIcon (filePath)
.then
(
(fileIcon) =>
{
let div = document.createElement ('div');
let img = document.createElement ('img');
img.setAttribute ('src', fileIcon.toDataURL ());
let size = fileIcon.getSize ();
img.setAttribute ('width', size.width);
img.setAttribute ('height', size.height);
div.appendChild (img);
div.appendChild (document.createTextNode (" " + path.basename (filePath)));
// For test purposes, add each file icon and name to the end of <body>
document.body.appendChild (div);
}
);
}
You may find some interesting hints about app.getFileIcon in the post: Is there a standard way for an Electron or Node.js app to access system-level icons?
Define a function for the same as:
function load(icon) {
if (cache[icon]) return cache[icon];
return cache[icon] = fs.readFileSync(__dirname + '/public/icons/' + icon, 'base64');
}
Here you can get the inspiration for the same.

Async and Sync Functions Node JS

I have a couple of functions I am running to download a zip file from Nexus, then unzip/extract the contents of the zip file, finally a search for a specific file type. All function work, however the synchronous search for some reason is not producing any results. If I simply run the download and extract functions in 1 script, then execute the search in another script I get my expected results. I am almost positive it is due to the search being synchronous whereas the download and extract are both async. Is there a quick way to add the find function at the end after the download & extract functions have run? Below is the code:
//npm modules
const fs = require('fs-extra');
const download = require('download');
const unzipper = require('unzipper');
const path = require('path');
//Custom Variables
const artifact = 'SOME_FILE.zip';
const repo = "SOME NEXUS REPOSITORY";
const url = "http://SOME URL/repository/";
const directory = 'SOME DIRECTORY';
//Get Artifact and Extract it to local directory function
const getArtifact = async () => {
const getArtifact = await download(url+repo, "./unzip")
const file = await fs.writeFileSync(directory+artifact, await download(url+repo))
const readStream = await fs.createReadStream(directory + artifact).pipe(unzipper.Extract({path:
directory}))
}
//Find function which should run after download and extract have been fulfilled
const findFile = function (dir, pattern) {
var results = [];
fs.readdirSync(dir).forEach(function (dirInner) {
dirInner = path.resolve(dir, dirInner);
var stat = fs.statSync(dirInner);
console.log(stat)
if (stat.isDirectory()) {
results = results.concat(findFile(dirInner, pattern));
}
if (stat.isFile() && dirInner.endsWith(pattern)) {
results.push(dirInner);
}
});
console.log(results)
return results;
};
//clear contents of directory before new download and extract
fs.emptyDirSync(directory)
//call download and extract function
getArtifact()
When I run "findFile" after the download & extract by itself in a separate script I get expected array output. However, when I try to incorporate (see below) this into the same script I get the an empty array:
getArtifact().then(function findFile (dir, pattern) {
var results = [];
fs.readdirSync(directory).forEach(function (dirInner) {
dirInner = path.resolve(directory, dirInner);
var stat = fs.statSync(dirInner);
console.log(stat)
if (stat.isDirectory()) {
results = results.concat(findFile(dirInner, pattern))
if (stat.isFile() && dirInner.endsWith(pattern)) {
results.push(dirInner);
}
}
console.log(results)
return results;
})
})
//Output
[]
//If I try the following:
getArtifact().then(findFile(directory, file))
// I get same empty array
[]
//If I run "findFile" in its own script after the download extract I get the following:
[
'SOME_FILE_PATH\\document1',
'SOME_FILE_PATH\\document2',
'SOME_FILE_PATH\\document3',
'SOME_FILE_PATH\\document4',
'SOME_FILE_PATH\\document5',
'SOME_FILE_PATH\\document6',
'SOME_FILE_PATH\\document7
]
Any help with how I can incorporate my findFile function into my existing download&extract function is appreciated...

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.

Categories

Resources