i'm using a recursive readdir to read the whole file tree of a directory to put it into a database.
My problem is that i'm trying to stop the next lines of code while the whole readdir / insert to the database isn't finished.
I was looking for a solution in promises but at the first call of my function (so in the first folder of the tree) the promise is fulfilled...
Any idea ?
function readsousdir(path, db, db2) {
var Datastore = require('nedb');
var fs = require('fs');
fs.readdir(path + '\\', function (err, files) {
files.forEach(function (file) {
fs.stat(path + '\\' + file, function (err, stats) {
var foldertrue = stats.isDirectory();
var filetrue = stats.isFile() == true;
if (foldertrue == true) {
var doc;
doc = folderdb(path + '\\' + file);
db2.insert(doc);
readsousdir(path + '\\' + file, db, db2);
}
if (filetrue) {
doc = pistedb(path + '\\' + file, []);
db.insert(doc);
}
});
});
});
}
Using BlueBird, you could use reduce:
var fs = Promise.promisifyAll(require("fs"));
function readsousdir(path, db, db2) {
var Datastore = require('nedb');
return fs.readdirAsync(path + '\\').reduce(function(_, file){
return fs.statAsync(path + '\\' + file)
.then(function(stats){
var foldertrue = stats.isDirectory();
var filetrue = stats.isFile() == true;
if (foldertrue == true) {
var doc;
doc = folderdb(path + '\\' + file);
db2.insert(doc);
return readsousdir(path + '\\' + file, db, db2)
}
if (filetrue) {
doc = pistedb(path + '\\' + file, []);
db.insert(doc);
}
});
});
}
Supposing your db library returns promises and you want to wait for the insertion, you would do
function readsousdir(path, db, db2) {
var Datastore = require('nedb');
return fs.readdirAsync(path + '\\').reduce(function(_, file){
return fs.statAsync(path + '\\' + file)
.then(function(stats){
var foldertrue = stats.isDirectory();
var filetrue = stats.isFile() == true;
if (foldertrue == true) {
var doc;
doc = folderdb(path + '\\' + file);
return db2.insert(doc).then(function(){
return readsousdir(path + '\\' + file, db, db2)
});
}
if (filetrue) {
doc = pistedb(path + '\\' + file, []);
return db.insert(doc);
}
});
});
}
Related
I have a requirement to execute several parallel functions
First functions:
Database get operation ie find inventory details from database
Second functions:
Process db results and save the result as a file
I am using two promise all, back to back to execute the above functions, I don't feel like this is the correct way to do this. Is there a
better way of handling these function calls. I am getting the result as per below code but want to know any other way.
Doing it following way:
let allQuery = {
sql: "SELECT * from inventory",
};
let inventoryQuery = {
sql: "SELECT * from inventory where inventory='1'",
};
let nearbyQuery = {
sql: "SELECT * from inventory where inventory='2",
};
let firstPromises = [dbService.fetch(allQuery),
dbService.fetch(inventoryQuery),
dbService.fetch(nearbyQuery)
];
Promise.all(firstPromises)
.then((values) => {
let all = values[0];
let inventory = values[1];
let nearby = values[2];
let fileKey1 = folderName + '/' + all.QueryExecutionId + '.csv';
let fileName1 = all.QueryExecutionId + '.csv';
let fileKey2 = folderName + '/' + inventory.QueryExecutionId + '.csv';
let fileName2 = inventory.QueryExecutionId + '.csv';
let fileKey3 = folderName + '/' + nearby.QueryExecutionId + '.csv';
let fileName3 = nearby.QueryExecutionId + '.csv';
let secondPromises = [s3Service.s3StreamDownload(bucketName, fileKey1, fileName1),
s3Service.s3StreamDownload(bucketName, fileKey2, fileName2),
s3Service.s3StreamDownload(bucketName, fileKey3, fileName3)
];
Promise.all(secondPromises)
.then((values) => {
console.log('Do later operation');
}).catch((error) => {
debug(`Error in promises ${error}`);
});
}).catch((error) => {
debug(`Error in promises ${error}`);
});
I think it can be more readable to extract the inner function and then chain them together:
Promise.all(firstPromises)
.then(transformToSecondPromises)
.then(Promise.all)
.then(values => {/* do later operation */})
.catch(error => { debug(`Error in promises ${error}`) })
function transformToSecondPromises ([all, inventory, nearby]) {
const fileKey1 = folderName + '/' + all.QueryExecutionId + '.csv';
const fileName1 = all.QueryExecutionId + '.csv';
const fileKey2 = folderName + '/' + inventory.QueryExecutionId + '.csv';
const fileName2 = inventory.QueryExecutionId + '.csv';
const fileKey3 = folderName + '/' + nearby.QueryExecutionId + '.csv';
const fileName3 = nearby.QueryExecutionId + '.csv';
return [
s3Service.s3StreamDownload(bucketName, fileKey1, fileName1),
s3Service.s3StreamDownload(bucketName, fileKey2, fileName2),
s3Service.s3StreamDownload(bucketName, fileKey3, fileName3)
];
}
As my question stats, I'm writing a javascript mp3 play that can upload, delete song files.
I write a server.js that creates a web service and can also manage song files, and a main.js that calls server.init().
But at the moment when ever I run the program it always says "Error: ENOENT: no such file or directory, open 'list.txt'"(list.txt is a file that contains the songs file names) after I run the program.
All my files are in the same folder as the main.js and server.js.
I just can't figure out where's wrong.
My codes are here:
var http = require('http');
var url = require('url');
var fs = require("fs");
var qs = require("querystring");
var formidable = require('formidable');
module.exports.init = function(port = 8000){
http.createServer(function (request, response){
var pathname = url.parse(request.url).pathname;
console.log(pathname);
switch(pathname){
case "/songInsert": songInsert(request, response); break;
case "/songDelete": songDelete(request, response); break;
case "/":
case "/index":
case "/song.html": index(request, response); break;
//case "/index.html":
//case "/default.html":
default:
fs.createReadStream(pathname)
.on("error", function(e){
console.log("Error: %s", e);
if(e.code == 'ENOENT'){
response.writeHead(200, {'Content-Type': 'text/html'});
response.write(pathname + 'File not found');
response.end();
}
else{
throw e;
}
})
.pipe(response);
}
}).listen(port, '127.0.0.1', function(){
console.log('HTTP listening at http://%s:%s/',
this.address().address, this.address().port);
});
};
function songInsert(request, response){
var form = new formidable.IncomingForm();
form.parse(request, function(err, fields, files){
var oldpath = files.songFile.path;
var newpath = '/' + files.songFile.name;
fs.rename(oldpath, newpath, function(err){
if(err) throw err;
fs.appendFileSync("list.txt", fields.songName + "," + files.songFile.name + "\n");
index(request, response);
});
});
}
function songDelete(request, response){
var no = qs.parse(url.parse(request.url).query).no;
var content = fs.readFileSync("list.txt", 'utf8');
var lines = content.split('\n');
fd = fs.openSync("list.txt", 'w');
lines.forEach(function(line, lineNo){
console.log(line);
if(line.length > 0)
if(lineNo == no){
let song = line.split(",");
fs.unlink("/" + song[1], function(err){
if(err) throw err;
console.log(song[1] + ' deleted!');
});
}
else{
fs.writeSync(fd, line + "\n");
}
});
fs.closeSync(fd);
index(request, response);
}
function index(request, response){
var content = fs.readFileSync("list.txt", 'utf8');
var lines = content.split('\n');
content = '';
lines.forEach(function(line, lineNo){
console.log("line: " + line);
if(line.length > 0){
let song = line.split(",");
content += '<tr>'
+ '<td>' + lineNo + '<td>'
+ '<td>' + song[0] + '<td>'
+ '<td>' + song[1] + '<td>'
+ '<td>'
+ '<input name = "btnPlay" type = "button" value = "播放" mp3file = "' + song[1] + '">'
+ '<input name = "btnDel" type = "button" value = "刪除" mp3no = "' + lineNo + '">'
+ '<td>'
+ '<tr>';
}
});
var html = fs.readFileSync("song.html", "utf8");
response.writeHead(200, {'Content-Type': 'text/html'});
response.write(html.replace("{{content}}", content));
response.end();
}
And here's the Node.js command prompt:
I have my selenium test set up to take screenshots, but they are not saving to the directory which I have specified. Can anybody show me what I am missing?
Here is how I am configuring the screenshots in the test:
function writeScreenshot(data, name) {
var fs = require('fs');
name = name || 'ss.png';
var screenshotPath = mkdirp(configuration.readSettings('screenshotDirectory') + fileNameURL + "/", function(err){});
fs.writeFileSync(screenshotPath + name, data, 'base64');
};
and then I take the screenshot:
driver.takeScreenshot().then(function(data) {
var screenshotFile = os + '_' + osVersion + '_' + browser + '_' + browserVersion + '.png';
writeScreenshot(data, screenshotFile);
});
The screenshots end up being saved instead in the projects root directory and with the file name preceded by 'undefined'. (ex. undefinedWindows_8_chrome_46.png)
It does, however, create the folders shown here: var screenshotPath = mkdirp(configuration.readSettings('screenshotDirectory') + fileNameURL + "/", function(err){});
So why is this happening?
mkdirp() is an async method. That is why you pass a callback. You will need to change your code to something like the following:
function writeScreenshot(data, name) {
var fs = require('fs');
name = name || 'ss.png';
var screenshotPath = configuration.readSettings('screenshotDirectory') + fileNameURL + "/";
mkdirp(screenshotPath, function(err){
if (err) {
// something else happened while creating the dir. You decide what to do
return;
}
// Otherwise (if dir was created)
fs.writeFileSync(screenshotPath + name, data, 'base64');
});
};
mkdirp() function is asynchronous - it creates a directory and returns nothing - this is why you having that leading undefined in the filename.
Save the file in the callback:
var screenshotPath = configuration.readSettings('screenshotDirectory') + fileNameURL + "/";
mkdirp(screenshotPath, function (err) {
if (!err) {
fs.writeFileSync(screenshotPath + name, data, 'base64');
} else {
// handle error
}
});
Or, synchronously create the directory and write to it this way:
var screenshotPath = configuration.readSettings('screenshotDirectory') + fileNameURL + "/";
if (mkdirp.sync(screenshotPath)) {
fs.writeFileSync(screenshotPath + name, data, 'base64');
}
My issue is downloading images with unknown extension( it may be 'png' or 'jpg' or 'bmp' or etc...).And I have some troubles with function chekHead's returning value:
var fs = require('fs'),
request = require('request');
var processImg = function (uri,filename){
if(checkHead(uri + 'png') > 2000){
download(uri + 'png', filename + '.png', function(){
console.log(uri + 'png' + " - downloaded")
})
}else if(checkHead(uri + 'jpg') > 2000){
download(uri + 'jpg', filename + '.jpg', function(){
console.log(uri + 'jpg' + " - downloaded")
})
}else if(checkHead(uri + 'bmp') > 2000) {
download(uri + 'bmp', filename + '.bmp', function () {
console.log(uri + 'bmp' + " - downloaded")
})
}
}
var checkHead = function(uri){
var length;
request.head(uri, function(err, res, body){
if(err) return console.log("Error");
length = res.headers['content-length'];
console.log(length);
});
return length;
}
var download = function(uri, filename, callback){
request(uri).pipe(fs.createWriteStream('./static/' + filename).on('close', callback));
};
So in checkHead function return length; always returns 'underfined', but console.log returns valid number; Why?
NodeJS executes your code in an asynchronous way using callbacks. Your return could happen before (in this case it's probably always the case) the callback is completed. The variable length is at the return undefined, because it hasn't received any values.
You could use promises to chain the function or you structure your code in another way.
For promises see e.g.:
async
q
var checkHead = function(uri){
var length;
// Callback is probably invoked after the return
request.head(uri, function(err, res, body){
if(err) return console.log("Error");
length = res.headers['content-length'];
console.log(length);
});
// gets executed directly
return length;
}
You have to use a callback so that is works the way you want it:
var checkHead = function(uri,callback){
request.head(uri, function(err, res, body){
if(err) return console.log("Error");
var length = res.headers['content-length'];
console.log(length);
callback(length);
});
};
Unfortunately because of your if-else logic I see no way at the moment to use promises(jquery) instead of callbacks and nested callbacks that can lead to callback-hell that is kind of bad pattern so I say sorry for this:
checkHead(uri + 'png',function(length){
if(length > 2000){
download(uri + 'png', filename + '.png', function(){
console.log(uri + 'png' + " - downloaded")
});
}
else{
checkHead(uri + 'jpg',function(length){
if(length > 2000){
download(uri + 'jpg', filename + '.jpg', function(){
console.log(uri + 'jpg' + " - downloaded")
});
}
else{
checkHead(uri + 'bmp',function(length){
if(length > 2000){
download(uri + 'jpg', filename + '.jpg', function(){
console.log(uri + 'jpg' + " - downloaded")
});
}
});
}
});
}
});
BUT
EcamScript 6 takes care of that. This is a good article about Generator Functions. And the main idea is to use yield for asynchronous methods or functions like request.head:
var checkHead = function*(uri){
var length = yield request.head(uri);
};
And use next to get length:
checkHead.next();//{value: 123, done: true}
This is only my concept i did not prove this but Generator Functions notations work in this way :)
I'm working on an app which needs to download some files from a webserver and store them on the device so they can be used offline. The code I'm using works fine in Android and iOS, however I'm having difficulty downloading more than one file on blackberry 10.
From looking at the web console it seems that the success callback is executed successfully for the first file, but not for any of the subsequent files. The fail callback is not being called for the subsequent files either.
I'm using cordova 2.9, and I've included all the required webworks plugins. I have set up access to my domain in config.xml and have set the access_shared permission.
I've also edited the cordova FileTransfer.download function to call the webworks download api "blackberry.io.filetransfer.download".
Below is the JavaScript code that I've written.
// Wait for device API libraries to load
//
document.addEventListener("deviceready", onDeviceReady, false);
// device APIs are available
//
function onDeviceReady() {
//replace list with your files you wish to download
var files = ["files/file1.txt", "files/file2.txt", "files/dir1/file3.txt"];
initiateDownload(files);
//getFSRoot(files);
}
var filesToDownload = 0;
var fileList = new Array();
var failedFileList;
var numFailedFiles = 0;
var currentFileIndex;
var retryCount = 0;
var root;
function isOnline(){
var bIsOnline = false;
if (navigator.connection.type != Connection.NONE && navigator.connection.type != Connection.UNKNOWN)
{
bIsOnline = true;
}
return bIsOnline;
}
function initiateDownload(files){
alert("initiate download");
failedFileList = new Array();
filesToDownload = files.length;
blackberry.io.sandbox = false;
for (i = 0; i < files.length; i++)
{
currentFileIndex = i;
console.log("initiate download of file index " + i + " File Name: " + files[i]);
getFile(files[i]);
}
}
function getFile(filePath)
{
//TODO: Do we need to make this function recursive to serialize asynchronous downloads?
console.log("START of function getFile() for " + filePath);
window.requestFileSystem(
LocalFileSystem.PERSISTENT, 0,
function onFileSystemSuccess(fileSystem)
{
console.log("Success getting filesystem for filePath: " + filePath);
createDirs(fileSystem.root, filePath, -1);
},
function(error){
console.log("Failed to get the filesystem for filePath: " + filePath);
}
);
}
function createDirs(parentDir, filePath, index)
{
console.log("createDirs params ===> parentDir=" + parentDir.toURL() + " filePath=" + filePath + " index=" + index);
arrDirs = filePath.split("/");
if (index >= (arrDirs.length - 1))
{
createFile(arrDirs[index], parentDir, filePath);
}
else
{
dirName = "myapp";
if (index >= 0)
{
dirName = arrDirs[index];
}
//if device is Blackberry, build up a full directory path as we are trying to install outside of sandbox
var path, dirToCreate = ""
if(device.platform = "BlackBerry"){
path = "myapp/";
console.log("JHPaths ======> arrDirs = " + arrDirs + " index = " +index);
for (i = 0; i <= index; i++){
path += arrDirs[i] + "/";
console.log("path = " + path + " i = " + i + " index = " + index);
}
dirToCreate = blackberry.io.home + "/" + path;
dirToCreate = dirToCreate.substring(0, dirToCreate.length - 1);
console.log("JHPaths Trying to create " + dirToCreate);
dirName = dirToCreate;
}
parentDir.getDirectory(dirName, {create: true, exclusive: false},
function (directoryEntry) {
console.log("Got directory " + directoryEntry.fullPath);
createDirs(directoryEntry, filePath, index + 1);
},
function (error) {console.log("Failed to get directory " + dirName + " Error code : " + error.code);});
}
}
function createFile(fileName, parentDir, filePath)
{
parentDir.getFile(
fileName, {create: true, exclusive: false},
function gotFileEntry(fileEntry)
{
localPath = fileEntry.fullPath;
if (isOnline())
{
console.log("Before remove");
fileEntry.remove(
function(){
console.log("FileEntry remove() was successful");
},
function(){
console.log("FileEntry remove() was failed");
}
);
console.log("After remove");
var fileTransfer = new FileTransfer();
//replace URL with the URL code you wish to download from
var baseURL = "http://<ip-address>/WebFolder/";
var uri = encodeURI(baseURL + filePath);
fullFilePath = blackberry.io.home + "/myapp/" + filePath;
fileTransfer.download(
uri,
/*localPath,*/
fullFilePath,
function(entry) {
console.log("download complete: " + entry.fullPath);
/*if(currentFileIndex < fileList.length){
currentFileIndex++;
}*/
if (device.platform == "iOS")
{
console.log("Setting file metadata");
parentDir.getFile(
fileName, {create:false, exclusive: false},
function gotFileEntry(fileEntry)
{
fileEntry.setMetaData(
function(){console.log("Set metadata for " + fileEntry.fullPath)},
function(){console.log("Set metadata failed for " + fileEntry.fullPath)},
{"com.apple.MobileBackup": 1}
);
},
function getFileFailed(fileError)
{
console.log("Get file failed:" + fileError.code);
}
);
}
filesToDownload--;
if (filesToDownload == 0 && failedFileList.length == 0)
{
//Call to retry failed Files
}
else if(failedFileList.length > 0){
if(retryCount < 1){
retryFailedFiles();
}
else{
}
}
},
function(error) {
console.log("Cache file download error source " + error.source);
console.log("Cache file download error target " + error.target);
console.log("Cache file download error code " + error.code);
//failedFileList[numFailedFiles++] = filePath;
failedFileList[numFailedFiles++] = filePath;
filesToDownload--;
if (filesToDownload == 0 && failedFileList.length == 0)
{
}
else if(failedFileList.length > 0){
if(retryCount < 1){
retryFailedFiles();
}
else{
}
}
}
);
}
},
function getFileFailed(fileError)
{
console.log("Create file failed:" + fileError.code);
filesToDownload--;
/*if (filesToDownload == 0)
{
callbackDeferred.resolve();
}*/
}
);
}
function retryFailedFiles(){
console.log("Retrying failed files");
retryCount++;
initiateDownload(failedFileList);
}