My current code is something like this.
var fs = require('fs')
fs.appendFile('log.txt', 'new data', function (err) {
if (err) {
// append failed
} else {
// done
}
})
But it creates a new file outside the folder. How do i make it edit the log.txt file inside my folder?
You can use __dirname
var fs = require('fs')
fs.appendFile(__dirname + '/log.txt', 'new data', function (err) {
if (err) {
// append failed
} else {
// done
}
})
What you could do is use path.resolve() to get the absolute path.
var fs = require('fs');
const path = require('path');
fs.appendFile(path.resolve('log.txt'), 'new data', function (err) {
if (err) {
// append failed
} else {
// done
}
})
Related
I'm new to node.js and trying to get files from FTP with ftp module and then I want to show files directory path in console. When I run server with npm start, I get all files(thats nice) but console returns empty array. If I run server again with npm start, console show every path(awesome), but not for new files. I have tried to solve my problem with callbacks and setTimeout with no result :C
var Client = require('ftp');
var c = new Client();
var path = require('path');
var fs = require('fs');
var dir = path.join(__dirname, '../excelTime/');
var files = fs.readdirSync(dir);
const connectionProperties = {
host: 'localhost',
user: 'user_geek',
port: '21'
}
Get files from ftp
const getFilesFromFTP = () => {
c.on('ready', function() {
c.list('./', function(err, list) {
if (err) throw err;
for(var i = 0; i < list.length; i++) {
(function(i) {
const name = list[i].name;
c.get('/' + name, function(err, stream) {
if (err) console.dir(err);
if(!err) {
stream.once('close', function() { c.end(); });
stream.pipe(fs.createWriteStream('./excelTime/' + name, {flags: 'w'}));
}
});
}).call(this, i);
}
c.end();
});
});
c.on('error', function(err) {
console.log(err)
});
c.connect(connectionProperties);
}
Get file path
const checkFolder = () => {
let promises = files.map(file => path.join(dir, file));
Promise.all(promises).then(console.log);
}
Call
setTimeout(function() {
checkFolder();
}, 3000);
getFilesFromFTP();
Here's the problem.
I have a global variable(Array type) named folders
let folders = [];
I modify it inside a Callback function inside yet another callback function.
Here's how.
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
files.forEach(file => {
const add = folder => folders.push(folder);
fs.lstat(path.join(dir, file), (err, stats) => {
if (err) throw err;
if (stats.isDirectory()) {
add(file);
}
console.log("INSIDE: " + folders);
});
console.log("OUTSITE: " + folders);
});
});
res.send(folders.length.toString());
});
Now the problem is, that when I read it on this line:
res.send(folders.length.toString());
The length is always 0.
And it is also 0 on console log line where I Print it with OUTSITE but it reads fine when I print it on the line where I mention it with INSIDE.
I know the problem after some search. It happens because the callback sets the variable on a later time in the event loop.(If it makes any sense, but you get the point).
I know the problem but I don't have any idea of how to solve it. I have tried various implementations including adding a global function that pushes to the array and calling it frpm inside the callback but the results are same.
Here's the full code.
const express = require("express");
const fs = require("fs");
const path = require("path");
const os = require("os");
// Initialize Express
const app = express();
// PORT on which the app process should be started
const PORT = process.env.PORT || 5100;
// Setting Up the path to Projects folder dynamically
// !Currently only works(tested) on the Darwin(MacOS) systems PS. I don't own a Windows
// TODO: Test on Windowsn and Linux
const homedir = os.homedir();
const dir = `${homedir}/Projects/`;
// TODO: Re-Write using Async/Await as it is not fully suppported as of Node version 10.0
let folders = [];
// Home Route
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
files.forEach(file => {
const add = folder => folders.push(folder);
fs.lstat(path.join(dir, file), (err, stats) => {
if (err) throw err;
if (stats.isDirectory()) {
add(file);
}
console.log("INSIDE: " + folders);
});
console.log("OUTSITE: " + folders);
});
});
res.send(folders.length.toString());
});
// Start the express server
app.listen(PORT, err => {
if (err) throw err;
console.log(`Project Lister Running On PORT: ${PORT}`);
});
Any solutions?
The issue here is that fs.lstat is asynchronous.
If you use the sync version fs.lstatSync, then you can call res.send after the forEach loop.
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
files.forEach(file => {
const add = folder => folders.push(folder);
try {
const stats = fs.lstatSync(path.join(dir, file))
if (stats.isDirectory()) {
add(file);
}
} catch (err) {
throw err
}
});
res.send(folders.length.toString());
})
})
Or for a non-blocking way you could use Promise.all:
app.get("/", (req, res) => {
// TODO: Proceed only if the path is correct and it is a directory
fs.readdir(dir, (err, files) => {
console.log("READING:");
if (err) throw err;
const promises = files.map(file => {
return new Promise((resolve, reject) => {
fs.lstat(path.join(dir, file), (err, stats) => {
if (err) {
reject(err);
}
if (stats.isDirectory()) {
add(file);
resolve();
}
console.log("INSIDE: " + folders);
});
});
});
Promise.all(promises, () => {
res.send(folders.length.toString());
});
});
});
So, here's the simplest solution I can find on my own!
#PeterN's answer is correct but could be hard to wrap a beginner's head around!
Here's my final code.
const express = require("express");
const fs = require("fs").promises; // !IMPORTANT Get the promises version of the File System Module
const path = require("path");
const os = require("os");
// Initialize Express
const app = express();
// PORT on which the app process should be started
const PORT = process.env.PORT || 5100;
// Setting Up the path to Projects folder dynamically
// !Currently only works(tested) on the Darwin(MacOS) systems PS. I don't own a Windows
// TODO: Test on Windows and Linux
const homedir = os.homedir();
const dir = `${homedir}/Projects/`;
// Home Route
app.get("/", async (req, res) => {
let folders = [];
// TODO: Proceed only if the path is correct and is a directory
try {
let files = await fs.readdir(dir);
for (let i = 0; i < files.length; i++) {
let file = await fs.lstat(path.join(dir, files[i]));
if (file.isDirectory()) {
folders.push(files[i]);
}
}
} catch (error) {
console.error(error);
}
res.send(folders);
});
// Start the express server
app.listen(PORT, err => {
if (err) throw err;
console.log(`Project Lister Running On PORT: ${PORT}`);
});
Take note, on the second line where I am importing the 'fs' modules, I now import it differently or rather say a different version!
I now import it as:
const fs = require("fs").promises;
The '.promises' added at the last imports the functions, methods of that module in their Promise based implementation. Thought you must note that it is stable only in version 11.x and up of NodeJs as of right now. I am using >12.x.
Now the rest of the process is rather straight forward assuming you are familiar with Async/Await and Promises. And if you're not I would highly suggest getting into it as it can save your day as it did with me.
Here's a great tutorial regarding it: Async/Await and Promise in JS
Ps. Use the for loop instead of 'array.forEach(e => //Do Something);' approach as it will again introduce the same problem as faced earlier because it is also callback-based!
Hope I helped you. Thanks!
I'm new to node.js & express.js, so... I want to upload multiple files, and later work with them.
But i need to send a response (ok or error status) after all my files have been saved on disk, or if one failed - then send an error callback.
Now I have such code:
var express = require('express');
var router = express.Router();
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
var fs = require('fs');
router.post('/upload', multipartMiddleware, function(req, res) {
var reqBody = req.body;
var reqFiles = req.files;
saveFile(reqFiles, 'main.xlsx', function(err) {
if (err) {
res.status(404).send('');
return;
}
res.send('Multi-File File uploaded');
}
});
function saveFile(file, name, callback) {
fs.writeFile('./uploads/' + name, file, callback);
}
but how can i change my code to parse this:
router.post('/upload', multipartMiddleware, function(req, res) {
var reqBody = req.body;
var reqFiles = req.files;
saveFile(reqFiles['files'][0], 'main.xlsx', function(err) {
if (err) {
res.status(404).send('');
return;
}
}
saveFile(reqFiles['files'][1], 'second.xlsx', function(err) {
if (err) {
res.status(404).send('');
return;
}
}
res.send(''); // only after first two fileUploaders have finished
});
function saveFile(file, name, callback) {
fs.writeFile('./uploads/' + name, file, callback);
}
You need to iterate through req.files. You can use async library.
For example:
async.each(req.files, function(file, callback) {
saveFile(file, file.name, callback)
}, function(err) {
res.send('')
})
The module recommends not using this. Use the multiparty module directly.
When you have the list of files they will also have file names. you can loop through those and save each file asynchronously. Then respond. Take a peek here
I want to get file information from all the directories once i have that i have further implementation of code, but i am stuck here and getting error that is pasted in question any idea what is implemented wrong in below code ?
cron.js
var fs = require('fs');
var path = require('path');
var async = require('async');
var directories = ['./logs/dit', './logs/st','./logs/uat']
function cronJob() {
directories.forEach(function(dir){
var files = fs.readdir(dir);
async.eachSeries(files, function(file,callback) {
var filePath = path.join(dirPath, file);
var fileInfo = {};
fs.stat(filePath, function(err, stats) {
if (err) {
console.info("File doesn't");
} else {
fileInfo.fileDate = stats.birthtime;
fileInfo.filename = file;
console.log(fileInfo);
// compareDates(fileInfo,filePath);
// callback();
}
});
});
})
}
cronJob();
Error
s\Ulog-0\ulog\app\serverfiles\logs\dit'
at Error (native)
at Object.fs.readdirSync (fs.js:808:18)
at C:\Users\WebstormProjects\Ulog-0\ulog\app\serverfiles\cronJob
20
at Array.forEach (native)
at cronJob (C:\Users\WebstormProjects\Ulog-0\ulog\app\serverfile
obs.js:7:13)
at Object.<anonymous> (C:\Users\\WebstormProjects\Ulog-0\ulog\app
readdir is asynchronous so you need to wait for its callback to execute further operations.
It's hard to know what exactly is the problem, so I have included log statements.
Edit
var fs = require('fs');
var path = require('path');
var async = require('async');
var directories = ['/../../logs/dit', '/../../logs/st', '/../../logs/uat'];
// loop through each directory
async.eachSeries(directories, function (dir, cb1) {
var dir = __dirname + dir;
console.log('reading', dir);
// get files for the directory
fs.readdir(dir, function (err, files) {
if (err) return cb1(err);
// loop through each file
async.eachSeries(files, function (file, cb2) {
var filePath = path.resolve(dir + '/' + file);
// get info for the file
fs.stat(filePath, function (err, stats) {
if (err) return cb2(err);
var fileInfo = { fileDate: stats.birthtime, filename: file };
console.log('fileInfo', fileInfo);
compareDates(fileInfo, filePath);
cb2(null, fileInfo);
});
}, cb1);
});
}, function (err, fileInfos) {
if (err) {
console.info('error', err);
return;
}
// when you're done reading all the files, do something...
});
I have some troubles using multer and promises (bluebird).
I try to upload a pdf file in a folder then extract the text inside this pdf with the plugin (textract)
Both of the functions I created works, but I have some trouble in the execution and the promisification of the upload function. here's my code :
pdf-rest.js :
var bodyParser = require('body-parser');
var request = require('request');
var jsonParser = bodyParser.json();
var fs = require('fs');
var Promise = require('bluebird');
var multer = require('multer');
var textract = require('textract');
module.exports = function(app) {
var destination = "uploads";
var filename = "" + Date.now() + ".pdf";
var filePath = "C:\\wamp64\\www\\ebook-stage\\backend\\rest\\uploads\\" + filename;
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, destination);
},
filename: function (req, file, cb) {
cb(null, filename);
}
});
app.post('/rest/create_pdf', function (req, res) {
var upload = multer({storage: storage}).single('file');
function uploadFile() {
var uploadFilePromise = new Promise(function (resolve, reject) {
upload(req, res, function (err) {
if (err) {
reject(err);
res.end('error uploading file')
}
else {
res.end('file uploaded');
console.log('fileupload')
}
});
resolve(upload);
});
console.log(uploadFilePromise);
return uploadFilePromise;
}
function textractPdf(path) {
textract.fromFileWithPath(path, function (error, text) {
console.log('textract');
if (text) {
console.log(text);
return text;
}
else {
console.log(error);
return error;
}
});
}
uploadFile().then(textractPdf(filePath));
});
server.js :
var express = require('express');
var app = express();
require('./pdf-rest.js')(app);
app.listen(8080);
I use a button to execute this script in a HTML page, the upload of the pdf in the folder work the first time I click on the button but the textract doesn't.
I know I do something wrong, the execution of the function textractPdf(Path) is done first. I think I didn't promisify correctly my upload function.