I tried and have looked at StackOverflow and the other posts are not answering my questions. This is a unique question. How can I get the path of the most recently uploaded file saved to a variable so it can be used later?
Code:
var pathtocheck = "C:\Users\user1\Downloads";
var path = require('path');
var fs = require('fs');
var getMostRecent = function (dir, cb) {
var dir = path.resolve(dir);
var files = fs.readdir(dir, function (err, files) {
var sorted = files.map(function(v) {
var filepath = path.resolve(dir, v);
return {
name:v,
time:fs.statSync(filepath).mtime.getTime()
};
})
.sort(function(a, b) { return b.time - a.time; })
.map(function(v) { return v.name; });
if (sorted.length > 0) {
cb(null, sorted[0]);
} else {
cb('Y U NO have files in this dir?');
}
})
}
await getMostRecent(pathtocheck, function (err, recent) {
if (err) console.error(err);
console.log(recent);
});
var lastdownloadedimage = ;
With callbacks, you have to write your code in the callback passed to getMostRecent, so
// Note: awaiting this takes no effect unless getMostRecent returns a promise.
getMostRecent(pathtocheck, function (err, recent) {
if (err) console.error(err);
console.log(recent);
var lastdownloadedimage = recent;
// Or just directly use `recent`
});
Or,
Async-await and Promise can also solve your issue, though I'm not sure how much you're familiar with them.
You can use Promisified version of file system API in Node.js v10 or above, by require('fs').promises instead of require('fs') (Documentation here)
Decalration of the functions like this:
// Also it's a good practice to use `const` or `let`, instead of `var`
const pathtocheck = "C:\Users\user1\Downloads";
const path = require('path');
const fs = require('fs');
const fsp = require('fs').promises;
// Decalre the function with `async` to use `await` in it
const getMostRecent = async function (dir) {
dir = path.resolve(dir);
const files = await fsp.readdir(dir)
const sorted = files.map(function(v) {
const filepath = path.resolve(dir, v);
return {
name:v,
time:fs.statSync(filepath).mtime.getTime()
// maybe you can use fsPromises.stat here
};
})
.sort(function(a, b) { return b.time - a.time; })
.map(function(v) { return v.name; });
if (sorted.length > 0) {
return sorted[0];
} else {
// Now you have no callbacks, so there are two options to return the error state.
// 1. Throw an Error with an error message
// 2. Return a special value such as `null` or `false`, which you can track it.
}
}; // <-- perhaps you need place a semicolon here.
And you call the function in async IIFE, wrapping anonymous async function to use await
(async function() {
const lastdownloadedimage = await getMostRecent(pathtocheck);
console.log(lastdownloadedimage)
})();
Or use Promise.then:
getMostRecent(pathtocheck).then(function(recent) {
var lastdownloadedimage = recent;
// Again, you can just directly use `recent`
})
Related
I am currently working with destructuring arrays in Javascript, I would like to access these variables in other functions but currently, I am struggling to figure out how I might go about this.
I've tried calling the function and then console.log(thermostatArray) -> I believe this returned pending promise
I've tried calling the function and awaiting it and then console.log thermostatArray.
dataFormat() is properly able to see log and use the array but heatCallCheck() is not and I am not seeing past the issue yet.
var express = require("express");
var router = express.Router();
const multer = require("multer");
var Excel = require("exceljs");
const index = require("../routes/index");
const workbook = new Excel.Workbook();
async function convertFile(workbook) {
csvWorkbook = workbook.csv.readFile("./uploads/uploadedFile.csv");
await csvWorkbook.then(async function(csvWorkbook) {
const worksheet = workbook.getWorksheet("sheet1");
try {
// await dataFormat(worksheet);
await heatCallCheck(worksheet,)
} catch (err) {
console.log(err);
}
await workbook.xlsx.writeFile("./uploads/convertedFile.xlsx").then(() => {
console.log("converted file written");
});
});
}
async function dataFormat(worksheet) {
let thermostatArray = []
await csvWorkbook.then(async function(worksheet) {
const serialNum = worksheet.getCell("D1").value;
const thermostatName = worksheet.getCell("D2").value;
const startDate = worksheet.getCell("D3").value;
const endDate = worksheet.getCell("D4").value;
const thermostat = worksheet.eachRow({includeEmpty: true}, function(row,rowNumber){
if (rowNumber > 6) {
thermostatArray.push(row.values)
}
})
console.log(`${thermostatArray[5]} Array Sample from dataFormat` )
console.log(`${thermostatArray[6]} Array Sample from dataFormat` )
return thermostatArray
})}
async function heatCallCheck(worksheet,thermostatArray) {
let test = await dataFormat(worksheet).then(thermostatArray => {
return thermostatArray[5]
}).catch(err => {
console.error(err)
})
console.log(`${test} result `)
}
My expected results, in this case, would be that I would be able to see the 4th element in thermostat array using the heatCallCheck() function.
I figured I would be able to access it after the .then is called.
my understanding is that .then(thermostatArray =>
makes that array the return value.
You do this:
async function someFunction() {
const myResultFromAPromise = await functionThatReturnsAPromise();
// ....do some stuff
return myResultFromAPromise;
}
OR
function someFunction() {
return functionThatReturnsAPromise().then(function(myResultFromAPromise) {
// ...do some stuff
return myResultFromAPromise;
});
}
but don't do both, it's just terribly confusing.
EDIT: as a commenter pointed out, you can await anything, but it's clear from your code that you're very confused about the point of async/await
I would like to get the pixels from some images and return them as an array. For the image handling I use https://www.npmjs.com/package/jimp . Jimp has an asynchronous function jimp.read(filePath) that needs to get handled with await. My image reader module:
const config = require('./configuration.json');
const fs = require('fs');
const path = require('path');
const jimp = require('jimp');
module.exports = readImages;
function readImages() { // Reads the image files and extracts the colors
const files = getFilesFromDirectory();
const imageFiles = filterForImageFiles(files);
return getInformationFromImageFiles(imageFiles);
}
function getFilesFromDirectory() { // Reads all the files from the directory provided from the configuration file
return fs.readdirSync(config.dirPath);
}
function filterForImageFiles(files) { // Filters an array of files for .png and .jpg files
return files.filter(file => {
const fileExtension = path.extname(file);
const isPngFile = fileExtension === '.jpg';
const isJpgFile = fileExtension === '.png';
return isPngFile || isJpgFile;
});
}
function getInformationFromImageFiles(imageFiles) { // Maps image files to image information
return imageFiles.map(imageFile => getInformationFromImageFile(imageFile));
}
async function getInformationFromImageFile(imageFile) { // Extracts information from an image file
const filePath = path.join(config.dirPath, imageFile);
const image = await jimp.read(filePath);
return getColorsFromImage(image);
}
function getColorsFromImage(image) { // Extracts the colors from an image file
const { width, height } = image.bitmap;
const colors = [,];
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const intColor = image.getPixelColor(x, y);
const rgbaColor = jimp.intToRGBA(intColor);
colors[x, y] = rgbaColor;
}
}
return colors;
}
When I run the code I receive an array with two items (because I provided two images). Both are Promise { <pending> }. Please have a look at getInformationFromImageFile which is an async function awaiting the jimp reader.
Why does it return a promise and does not resolve it? Do I have to await every function and the whole module ... ?
As getInformationFromImageFile is marked async it will return a Promise therefore it must be awaited. You need to await where it is called. These changes should fix it:
async function getInformationFromImageFiles(imageFiles) {
const imageInfos = [];
for (let i = 0; i < imageFiles.length; i++) {
const imageFile = imageFiles[i];
imageInfos.push(await getInformationFromImageFile(imageFile));
}
return imageInfos;
}
async function readImages() {
const files = getFilesFromDirectory();
const imageFiles = filterForImageFiles(files);
return await getInformationFromImageFiles(imageFiles);
}
You also need to await the function getInformationFromImageFile().
async/await is just like promises. You gotta handle your async returns like you handle promises.
Whenever you invoke an async function, you gotta await it in another async function or use .then() like you do with promises.
// make this function async
async function readImages() {
// Reads the image files and extracts the colors
const files = getFilesFromDirectory();
const imageFiles = filterForImageFiles(files);
// the next line is an async call - so await it
const images = await getInformationFromImageFiles(imageFiles); // array of images
return images;
}
function getFilesFromDirectory() {
// Reads all the files from the directory provided from the configuration file
return fs.readdirSync(config.dirPath);
}
function filterForImageFiles(files) {
// Filters an array of files for .png and .jpg files
return files.filter((file) => {
const fileExtension = path.extname(file);
const isPngFile = fileExtension === '.jpg';
const isJpgFile = fileExtension === '.png';
return isPngFile || isJpgFile;
});
}
// make this function async
async function getInformationFromImageFiles(imageFiles) {
// promisify all async returns
return Promise.all(imageFiles.map((imageFile) => getInformationFromImageFile(imageFile)));
}
// return async
async function getInformationFromImageFile(imageFile) {
// Extracts information from an image file
const filePath = path.join(config.dirPath, imageFile);
const image = await jimp.read(filePath);
return getColorsFromImage(image);
}
Async/Await always returns Promise, so you can do something like this:
Promise
.all(readImages())
.then(imd => console.log(imd))
.catch(error => console.log(error));`
As other member said, your function needs to be awaited hence it will return the promise result.
If you want to avoid awaiting the function, you can get your promise result in a synchronous way, like this:
let x = new Promise(function(){
//code
});
x.then(function(data){
//Promise resolved, do something
}).then(function(err){
//Promise rejected, do something
});
there is a function that I use to read all files in a directory and then sent an object with emitter to the client.
this is my code that works fine,
const getFilesList = (path, emitter) => {
fs.readdir(path, (err, files) => {
emitter('getFileList', files);
});
};
but when I want to filter hidden files with this code, the 'standardFolders' will send empty in the emitter.
const getFilesList = (path, emitter) => {
let standardFolders = [];
fs.readdir(path, (err, files) => {
if (files) {
files.map((file) => {
winattr.get(path + file, function (err, attrs) {
if (err == null && attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
});
});
} else {
standardFolders = null;
}
emitter('getFileList', standardFolders);
});
};
what is wrong with my code in the second part?
winattr.get(filepath,callback) is asynchronous, so imagine your code "starts" the file.map() line and then immediately skips to emitter('getFileList',standardFolders) --- which standardFolders is empty because it hasn't finished yet!
You can use a library like async.io to handle your callback functions, or you can use a counter and keep track of when all of the callbacks (for each file) has finished yourself.
Example:
// an asynchronous function because setTimeout
function processor(v,cb){
let delay = Math.random()*2000+500;
console.log('delay',delay);
setTimeout(function(){
console.log('val',v);
cb(null,v);
},delay);
}
const main = function(){
const list = ['a','b','c','d'];
let processed = [];
let count = 0;
console.log('starting');
list.map(function(v,i,a){
console.log('calling processor');
processor(v,function(err,value){
processed.push(v);
count+=1;
console.log('count',count);
if(count>=list.length){
// all are finished, continue on here.
console.log('done');
}
})
})
console.log('not done yet!');
};
main();
Similarly, for your code:
const getFilesList = (path, emitter) => {
let standardFolders = [];
fs.readdir(path, (err, files) => {
if (files) {
let count = 0;
files.map((file) => {
winattr.get(path + file, function (err, attrs) {
if (err == null && attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
count+=1;
if(count>=files.length){
// finally done
emitter('getFileList', standardFolders);
}
});
});
} else {
standardFolders = null;
emitter('getFileList', standardFolders);
}
});
};
As already sayed in the other answer winattr.get is async, so the loop finishes before any of the callbacks of winattr.get is called.
You could convert your code using async/await and primitify into a code that looks almost like a sync version, and you can completely get rid of the callbacks or counters
const {promisify} = require('util')
const readdir = promisify(require('fs').readdir)
const winattrget = promisify(require('winattr').get)
const getFilesList = async (path, emitter) => {
let standardFolders = [];
try {
let files = await readdir(path);
for (let file of files) {
try {
let attrs = await winattrget(path + file)
if (attrs.directory && (!attrs.hidden && !attrs.system)) {
standardFolders.push(file)
}
} catch (err) {
// do nothing if an error occurs
}
}
} catch (err) {
standardFolders = null;
}
emitter('getFileList', standardFolders);
};
An additional note: In your code you write files.map, but mapping is use to transform the values of a given array and store them in a new one, and this is not done in your current code, so in the given case you should use a forEach loop instead of map.
I want to return the files of a directory.
I need it to pass the route to another function.
In other words, how can I return the files of a directory using JavaScript/Node.js?
const fs = require('fs');
const path = require('path');
const mdLinks = require('../index');
exports.extension = (route) => {
return new Promise((resolve, reject) => {
try {
recursive(route);
} catch (e) {
reject(e);
}
});
}
const recursive = (route) => {
const extMd = ".md";
let extName = path.extname(route);
let files = [];
fs.stat(route, (err, stats) => {
if (stats && stats.isDirectory()) {
fs.readdir(route, (err, files) => {
files.forEach(file => {
let reFile = path.join(route, file);
if (file !== '.git') {
recursive(reFile);
}
});
})
}
else if (stats.isFile() && (extMd == extName)) {
files.push(route);
}
})
return files;
}
There are multiple problems.
First off, your function is asynchronous so it cannot just return the files value because your function returns long before anything is added to the files array (that's one reason that it's empty). It will have to either call a callback when its done or return a promise that the caller can use. You will have to fix that in both the top level and when you call yourself recursively.
Second, you are redefining files at each recursive step so you have no way to collect them all. You can either pass in the array to add to or you can define the files array at a higher level where everyone refers to the same one or you can have the call to recursive concat the files that are returned to your current array.
Third, you haven't implemented any error handling on any of your asynchronous file I/O calls.
Here's my recommended way of solving all these issue:
exports.extension = (route) => {
return recursive(route);
}
const util = require('util');
const stat = util.promisify(fs.stat);
const readdir = util.promisify(fs.readdir);
// returns a promise that resolves to an array of files
async function recursive(route) {
const extMd = ".md";
let extName = path.extname(route);
let files = [];
let stats = await stat(route);
if (stats.isDirectory()) {
let dirList = await readdir(route);
for (const file of dirList) {
if (file !== '.git') {
let reFile = path.join(route, file);
let newFiles = await recursive(reFile);
// add files onto the end of our list
files.push(...newFiles);
}
}
} else if (stats.isFile() && extMd === extName) {
files.push(route);
}
// make the files array be the resolved value
return files;
});
I've used the following code to call two modules, but the invoke action is called before the validate file (I saw in debug). What I should do to verify that validateFile is called before appHandler.invokeAction? Should I use a promise?
var validator = require('../uti/valid').validateFile();
var appHandler = require('../contr/Handler');
appHandler.invokeAction(req, res);
Update
this is the validate file code
var called = false;
var glob = require('glob'),
fs = require('fs');
module.exports = {
validateFile: function () {
glob("myfolder/*.json", function (err, files) {
var stack = [];
files.forEach(function (file) {
fs.readFile(file, 'utf8', function (err, data) { // Read each file
if (err) {
console.log("cannot read the file", err);
}
var obj = JSON.parse(data);
obj.action.forEach(function (crud) {
for (var k in crud) {
if (_inArray(crud[k].path, stack)) {
console.log("duplicate founded!" + crud[k].path);
break;
}
stack.push(crud[k].path);
}
})
});
});
});
}
};
Because glob and fs.readFile are async functions and appHandler.invokeAction is invoked during i/o from disk.
Promise is a good solution to solve this but an old school callback could do the job.
validator.validateFile().then(function() {
appHandler.invokeAction(req, res);
});
and for validate
var Promise = require("bluebird"), // not required if you are using iojs or running node with `--harmony`
glob = require('mz/glob'),
fs = require('mz/fs');
module.exports = {
validateFile: function () {
return glob("myfolder/*.json").then(function(files) {
return Promise.all(files.map(function(file) {
// will return an array of promises, if any of them
// is rejected, validateFile promise will be rejected
return fs.readFile(file).then(function (content) {
// throw new Error(''); if content is not valid
});
}));
})
}
};
If you want working with promise mz could help :)
As the fs.fileRead is async, you should put the code that you want to execute after validateFile to its callback.
The origin could be:
var validator = require('../uti/valid').validateFile();
var appHandler = require('../contr/Handler');
// create a new function that when execute, will call appHandler.invokeAction with req and res given to its arguments.
validator.validateFile(appHandler.invokeAction.bind(null, req, res));
The validator part should be:
var called = false;
var glob = require('glob'),
fs = require('fs');
module.exports = {
validateFile: function (callback) {
glob("myfolder/*.json", function (err, files) {
var stack = [];
// Use it to decide whether all files processed or not.
var filesToLoad = files.length;
files.forEach(function (file) {
fs.readFile(file, 'utf8', function (err, data) { // Read each file
--filesToLoad;
if (err) {
console.log("cannot read the file", err);
// If the invoke action doesn't depend on the result. You may want to call it here too.
}
var obj = JSON.parse(data);
obj.action.forEach(function (crud) {
for (var k in crud) {
if (_inArray(crud[k].path, stack)) {
console.log("duplicate founded!" + crud[k].path);
break;
}
stack.push(crud[k].path);
}
})
// Only called the callback after all files processed.
if (filesToLoad === 0) {
callback();
}
});
});
});
}
};
Edit: Thanks for Bergi's remind that there's the files are an array and you have to call the callback when all files is processed, so we have to further use a variable to decide how many files are not processed yet.