我不知道这是否可行,但是这里有。使用回调会使其变得更加困难。
我有一个带有html文件的目录,我想通过node.js和socket.io以Object块的形式发送回客户端。
我的所有文件都在/ tmpl
所以socket需要读取/ tmpl中的所有文件。
对于每个文件,它必须将数据存储在以文件名作为键的对象中,并将内容作为值存储。
var data;
// this is wrong because it has to loop trough all files.
fs.readFile(__dirname + '/tmpl/filename.html', 'utf8', function(err, html){
if(err) throw err;
//filename must be without .html at the end
data['filename'] = html;
});
socket.emit('init', {data: data});
最终的回调也是错误的。当目录中的所有文件都完成时,必须调用它。
但我不知道如何创建代码,任何人都知道这是否可能?
答案 0 :(得分:132)
所以,有三个部分。阅读,存储和发送。
这是阅读部分:
var fs = require('fs');
function readFiles(dirname, onFileContent, onError) {
fs.readdir(dirname, function(err, filenames) {
if (err) {
onError(err);
return;
}
filenames.forEach(function(filename) {
fs.readFile(dirname + filename, 'utf-8', function(err, content) {
if (err) {
onError(err);
return;
}
onFileContent(filename, content);
});
});
});
}
这是存储部分:
var data = {};
readFiles('dirname/', function(filename, content) {
data[filename] = content;
}, function(err) {
throw err;
});
发送部分取决于您。您可能希望逐个发送或在阅读完成后发送它们。
如果要在读取完成后发送文件,则应使用fs
函数的同步版本或使用promises。异步回调不是一种好的风格。
此外,您询问有关剥离扩展程序的问题。你应该逐一处理问题。没有人会为您编写完整的解决方案。
答案 1 :(得分:16)
这是前一个版本的现代Promise
版本,使用Promise.all
方法在读取所有文件时解析所有承诺:
/**
* Promise all
* @author Loreto Parisi (loretoparisi at gmail dot com)
*/
function promiseAllP(items, block) {
var promises = [];
items.forEach(function(item,index) {
promises.push( function(item,i) {
return new Promise(function(resolve, reject) {
return block.apply(this,[item,index,resolve,reject]);
});
}(item,index))
});
return Promise.all(promises);
} //promiseAll
/**
* read files
* @param dirname string
* @return Promise
* @author Loreto Parisi (loretoparisi at gmail dot com)
* @see http://stackoverflow.com/questions/10049557/reading-all-files-in-a-directory-store-them-in-objects-and-send-the-object
*/
function readFiles(dirname) {
return new Promise((resolve, reject) => {
fs.readdir(dirname, function(err, filenames) {
if (err) return reject(err);
promiseAllP(filenames,
(filename,index,resolve,reject) => {
fs.readFile(path.resolve(dirname, filename), 'utf-8', function(err, content) {
if (err) return reject(err);
return resolve({filename: filename, contents: content});
});
})
.then(results => {
return resolve(results);
})
.catch(error => {
return reject(error);
});
});
});
}
如何使用
就像这样简单:
readFiles( EMAIL_ROOT + '/' + folder)
.then(files => {
console.log( "loaded ", files.length );
files.forEach( (item, index) => {
console.log( "item",index, "size ", item.contents.length);
});
})
.catch( error => {
console.log( error );
});
假设您有另一个文件夹列表,您也可以遍历此列表,因为内部promise.all将以异步方式解析每个文件夹:
var folders=['spam','ham'];
folders.forEach( folder => {
readFiles( EMAIL_ROOT + '/' + folder)
.then(files => {
console.log( "loaded ", files.length );
files.forEach( (item, index) => {
console.log( "item",index, "size ", item.contents.length);
});
})
.catch( error => {
console.log( error );
});
});
工作原理
promiseAll
做了神奇的事。它需要一个签名function(item,index,resolve,reject)
的功能块,其中item
是数组中的当前项,index
它在数组中的位置,resolve
和reject
Promise
回调函数。
每个promise将被推送到当前index
的数组中,并通过匿名函数调用以当前item
作为参数:
promises.push( function(item,i) {
return new Promise(function(resolve, reject) {
return block.apply(this,[item,index,resolve,reject]);
});
}(item,index))
然后所有的承诺都将得到解决:
return Promise.all(promises);
答案 2 :(得分:6)
const fs = require('fs');
const path = require('path');
function readFiles(dir, processFile) {
// read directory
fs.readdir(dir, (error, fileNames) => {
if (error) throw error;
fileNames.forEach(filename => {
// get current file name
const name = path.parse(filename).name;
// get current file extension
const ext = path.parse(filename).ext;
// get current file path
const filepath = path.resolve(dir, filename);
// get information about the file
fs.stat(filepath, function(error, stat) {
if (error) throw error;
// check if the current path is a file or a folder
const isFile = stat.isFile();
// exclude folders
if (isFile) {
// callback, do something with the file
processFile(filepath, name, ext, stat);
}
});
});
});
}
用法:
// use an absolute path to the folder where files are located
readFiles('absolute/path/to/directory/', (filepath, name, ext, stat) => {
console.log('file path:', filepath);
console.log('file name:', name);
console.log('file extension:', ext);
console.log('file information:', stat);
});
/**
* @description Read files synchronously from a folder, with natural sorting
* @param {String} dir Absolute path to directory
* @returns {Object[]} List of object, each object represent a file
* structured like so: `{ filepath, name, ext, stat }`
*/
function readFilesSync(dir) {
const files = [];
fs.readdirSync(dir).forEach(filename => {
const name = path.parse(filename).name;
const ext = path.parse(filename).ext;
const filepath = path.resolve(dir, filename);
const stat = fs.statSync(filepath);
const isFile = stat.isFile();
if (isFile) files.push({ filepath, name, ext, stat });
});
files.sort((a, b) => {
// natural sort alphanumeric strings
// https://stackoverflow.com/a/38641281
return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' });
});
return files;
}
用法:
// return an array list of objects
// each object represent a file
const files = readFilesSync('absolute/path/to/directory/');
const { promisify } = require('util');
const readdir_promise = promisify(fs.readdir);
const stat_promise = promisify(fs.stat);
function readFilesAsync(dir) {
return readdir_promise(dir, { encoding: 'utf8' })
.then(filenames => {
const files = getFiles(dir, filenames);
return Promise.all(files);
})
.catch(err => console.error(err));
}
function getFiles(dir, filenames) {
return filenames.map(filename => {
const name = path.parse(filename).name;
const ext = path.parse(filename).ext;
const filepath = path.resolve(dir, filename);
return stat({ name, ext, filepath });
});
}
function stat({ name, ext, filepath }) {
return stat_promise(filepath)
.then(stat => {
const isFile = stat.isFile();
if (isFile) return { name, ext, filepath, stat };
})
.catch(err => console.error(err));
}
用法:
readFiles('absolute/path/to/directory/')
// return an array list of objects
// each object is a file
// with those properties: { name, ext, filepath, stat }
.then(files => console.log(files))
.catch(err => console.log(err));
注意:为文件夹返回undefined
,如果您愿意,可以将其过滤掉:
readFiles('absolute/path/to/directory/')
.then(files => files.filter(file => file !== undefined))
.catch(err => console.log(err));
答案 3 :(得分:3)
你是否像我这样的懒人并且爱 npm模块:D然后检查出来。
npm install node-dir
阅读文件的示例:
var dir = require('node-dir');
dir.readFiles(__dirname,
function(err, content, next) {
if (err) throw err;
console.log('content:', content); // get content of files
next();
},
function(err, files){
if (err) throw err;
console.log('finished reading files:', files); // get filepath
});
答案 4 :(得分:2)
如果您有Node.js 8或更高版本,则可以使用新的util.promisify。 (我将代码中与重新格式化为对象的部分标记为可选,原始帖子要求这样做。)
const fs = require('fs');
const { promisify } = require('util');
let files; // optional
promisify(fs.readdir)(directory).then((filenames) => {
files = filenames; // optional
return Promise.all(filenames.map((filename) => {
return promisify(fs.readFile)(directory + filename, {encoding: 'utf8'});
}));
}).then((strArr) => {
// optional:
const data = {};
strArr.forEach((str, i) => {
data[files[i]] = str;
});
// send data here
}).catch((err) => {
console.log(err);
});
答案 5 :(得分:1)
为使代码在不同的环境中顺畅工作, path.resolve 可用于操作路径的地方。这是更好的代码。
阅读部分:
var fs = require('fs');
function readFiles(dirname, onFileContent, onError) {
fs.readdir(dirname, function(err, filenames) {
if (err) {
onError(err);
return;
}
filenames.forEach(function(filename) {
fs.readFile(path.resolve(dirname, filename), 'utf-8', function(err, content) {
if (err) {
onError(err);
return;
}
onFileContent(filename, content);
});
});
});
}
存储部分:
var data = {};
readFiles(path.resolve(__dirname, 'dirname/'), function(filename, content) {
data[filename] = content;
}, function(error) {
throw err;
});
答案 6 :(得分:1)
另一个版本采用Promise的现代方法。基于Promise的其他响应时间更短:
const readFiles = (dirname) => {
const readDirPr = new Promise( (resolve, reject) => {
fs.readdir(dirname,
(err, filenames) => (err) ? reject(err) : resolve(filenames))
});
return readDirPr.then( filenames => Promise.all(filenames.map((filename) => {
return new Promise ( (resolve, reject) => {
fs.readFile(dirname + filename, 'utf-8',
(err, content) => (err) ? reject(err) : resolve(content));
})
})).catch( error => Promise.reject(error)))
};
readFiles(sourceFolder)
.then( allContents => {
// handle success treatment
}, error => console.log(error));
答案 7 :(得分:1)
我只是写了这个,对我来说看起来更干净:
const fs = require('fs');
const util = require('util');
const readdir = util.promisify(fs.readdir);
const readFile = util.promisify(fs.readFile);
const readFiles = async dirname => {
try {
const filenames = await readdir(dirname);
console.log({ filenames });
const files_promise = filenames.map(filename => {
return readFile(dirname + filename, 'utf-8');
});
const response = await Promise.all(files_promise);
//console.log({ response })
//return response
return filenames.reduce((accumlater, filename, currentIndex) => {
const content = response[currentIndex];
accumlater[filename] = {
content,
};
return accumlater;
}, {});
} catch (error) {
console.error(error);
}
};
const main = async () => {
const response = await readFiles(
'./folder-name',
);
console.log({ response });
};
您可以根据需要修改response
格式。
此代码中的response
格式如下:
{
"filename-01":{
"content":"This is the sample content of the file"
},
"filename-02":{
"content":"This is the sample content of the file"
}
}
答案 8 :(得分:0)
import matplotlib
matplotlib.use("MacOSX")
from matplotlib import pyplot as plt
plt.plot(range(10)) # No interactive plot, as expected
%pylab
# This outputs the following lines:
# Using matplotlib backend: MacOSX
# Populating the interactive namespace from numpy and matplotlib
plt.plot(range(10)) # Interactive plot!
谁想尝试使用发电机?