Não sei se isso é possível, mas aqui vai. E trabalhar com retornos de chamada torna isso ainda mais difícil.
Eu tenho um diretório com arquivos html que desejo enviar de volta ao cliente em blocos de objeto com node.js e socket.io.
Todos os meus arquivos estão em / tmpl
Portanto, o socket precisa ler todos os arquivos em / tmpl.
para cada arquivo, ele deve armazenar os dados em um objeto com o nome do arquivo como a chave e o conteúdo como o valor.
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});
O retorno de chamada final também está errado. Ele deve ser chamado quando todos os arquivos do diretório estiverem prontos.
Mas não sei criar o código, alguém sabe se isso é possibel?
readfileSync
ereaddirSync
. nodejs.org/docs/v0.4.8/api/fs.html#fs.readdirSyncRespostas:
Portanto, existem três partes. Ler, armazenar e enviar.
Aqui está a parte da leitura:
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); }); }); }); }
Aqui está a parte de armazenamento:
var data = {}; readFiles('dirname/', function(filename, content) { data[filename] = content; }, function(err) { throw err; });
O envio é com você. Você pode querer enviá-los um por um ou após a conclusão da leitura.
Se você deseja enviar arquivos após a conclusão da leitura, você deve usar as versões de sincronização do
fs
funções ou usar promessas. Callbacks assíncronos não é um bom estilo.Além disso, você perguntou sobre a remoção de uma extensão. Você deve prosseguir com as perguntas uma por uma. Ninguém escreverá uma solução completa só para você.
fonte
0===--c
faz.c--
e entãoif (c===0)
isso é o mesmo. Ele só diminuic
por1
e verifica se ele chegou a zeroif(c===files.length)
algo assim.readFile
html seja retornado, entãoc
deve aumentar parax
(número de arquivos) imediatamente e, em seguida, diminuir quando o html chega do disco (o que é muito mais tarde)Esta é uma
Promise
versão moderna da anterior, usando umaPromise.all
abordagem para resolver todas as promessas quando todos os arquivos foram lidos:/** * 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); }); }); }); }
Como usá-lo:
Tão simples quanto fazer:
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 ); });
Suponha que você tenha outra lista de pastas que também pode iterar sobre esta lista, uma vez que a promessa interna. Tudo resolverá cada uma delas de forma assíncrona:
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 ); }); });
Como funciona
O
promiseAll
faz a mágica. Leva um bloco de função de assinaturafunction(item,index,resolve,reject)
, ondeitem
é o item atual na matriz,index
sua posição na matrizresolve
ereject
asPromise
funções de retorno de chamada. Cada promessa será enviada em uma matriz no atualindex
e com o atualitem
como argumentos por meio de uma chamada de função anônima:promises.push( function(item,i) { return new Promise(function(resolve, reject) { return block.apply(this,[item,index,resolve,reject]); }); }(item,index))
Então todas as promessas serão resolvidas:
return Promise.all(promises);
fonte
return block(item,index,resolve,reject);
vez dereturn block.apply(this,[item,index,resolve,reject]);
, acho queapply
torna mais difícil de entender - há algum benefício que eu não esteja ciente?Para todos os exemplos abaixo, você precisa importar módulos fs e path :
const fs = require('fs'); const path = require('path');
Leia arquivos de forma assíncrona
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); } }); }); }); }
Uso:
// 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); });
Leia arquivos de forma síncrona, armazene em array, classificação natural
/** * @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; }
Uso:
// return an array list of objects // each object represent a file const files = readFilesSync('absolute/path/to/directory/');
Leia arquivos assíncronos usando promessa
Mais informações sobre promisify neste artigo .
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)); }
Uso:
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));
Nota: volte
undefined
para as pastas, se quiser, pode filtrá-las:readFiles('absolute/path/to/directory/') .then(files => files.filter(file => file !== undefined)) .catch(err => console.log(err));
fonte
Você é uma pessoa preguiçosa como eu e adora o módulo npm : D então veja isso.
npm install node-dir
exemplo para ler arquivos:
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 });
fonte
Se você tiver o Node.js 8 ou posterior, pode usar o novo util.promisify. (Estou marcando como opcionais as partes do código que têm a ver com a reformatação como um objeto, que a postagem original solicitou.)
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); });
fonte
Outra versão com o método moderno da Promise. É mais curto que as outras respostas com base na promessa:
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));
fonte
Para que o código funcione sem problemas em ambientes diferentes, path.resolve pode ser usado em locais onde o path é manipulado. Aqui está o código que funciona melhor.
Parte de leitura:
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); }); }); }); }
Armazenando parte:
var data = {}; readFiles(path.resolve(__dirname, 'dirname/'), function(filename, content) { data[filename] = content; }, function(error) { throw err; });
fonte
Acabei de escrever isso e parece mais claro para mim:
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 }); };
Você pode modificar o
response
formato de acordo com sua necessidade. Oresponse
formato deste código será semelhante a:{ "filename-01":{ "content":"This is the sample content of the file" }, "filename-02":{ "content":"This is the sample content of the file" } }
fonte
assíncrono / esperar
const { promisify } = require("util") const directory = path.join(__dirname, "/tmpl") const pathnames = promisify(fs.readdir)(directory) try { async function emitData(directory) { let filenames = await pathnames var ob = {} const data = filenames.map(async function(filename, i) { if (filename.includes(".")) { var storedFile = promisify(fs.readFile)(directory + `\\${filename}`, { encoding: "utf8", }) ob[filename.replace(".js", "")] = await storedFile socket.emit("init", { data: ob }) } return ob }) } emitData(directory) } catch (err) { console.log(err) }
Quem quer experimentar geradores?
fonte