No sé si esto es posible, pero aquí va. Y trabajar con devoluciones de llamada lo hace aún más difícil.
Tengo un directorio con archivos html que quiero enviar al cliente en fragmentos de objetos con node.js y socket.io.
Todos mis archivos están en / tmpl
Entonces, socket necesita leer todos los archivos en / tmpl.
para cada archivo, debe almacenar los datos en un objeto con el nombre del archivo como clave y el contenido como 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});
La devolución de llamada final también es incorrecta. Tiene que ser llamado cuando todos los archivos en el directorio estén listos.
Pero no sé cómo crear el código, ¿alguien sabe si esto es posible?
readfileSync
yreaddirSync
. nodejs.org/docs/v0.4.8/api/fs.html#fs.readdirSyncRespuestas:
Entonces, hay tres partes. Leer, almacenar y enviar.
Aquí está la parte de lectura:
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); }); }); }); }
Aquí está la parte de almacenamiento:
var data = {}; readFiles('dirname/', function(filename, content) { data[filename] = content; }, function(err) { throw err; });
La parte de envío depende de usted. Es posible que desee enviarlos uno por uno o después de completar la lectura.
Si desea enviar archivos después de completar la lectura, debe usar versiones sincronizadas de
fs
funciones o promesas. Las devoluciones de llamada asíncronas no son un buen estilo.Además, preguntaste sobre la eliminación de una extensión. Debe proceder con las preguntas una por una. Nadie escribirá una solución completa solo para ti.
fuente
0===--c
Aunque una cosa, ¿puedes explicar qué hace?c--
y luegoif (c===0)
eso es lo mismo. Sólo disminuyec
por1
y comprueba si se llegó a ceroif(c===files.length)
algo así?readFile
se devuelva el primer html, por lo quec
debería subir ax
(número de archivos) inmediatamente y luego disminuir cuando el html llega desde el disco (que es mucho más tarde)Esta es una
Promise
versión moderna de la anterior, que utiliza unPromise.all
enfoque para resolver todas las promesas cuando se han leído todos los archivos:/** * 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); }); }); }); }
Cómo usarlo:
Tan simple como hacer:
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 ); });
Supongamos que tiene otra lista de carpetas, también puede iterar sobre esta lista, ya que la promesa interna.all resolverá cada una de ellas de forma asincrónica:
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 ); }); });
Cómo funciona
El
promiseAll
hace la magia. Toma un bloque de funciones de firmafunction(item,index,resolve,reject)
, dondeitem
está el elemento actual en la matriz,index
su posición en la matrizresolve
yreject
lasPromise
funciones de devolución de llamada. Cada promesa se insertará en una matriz en la actualindex
y con la actualitem
como argumentos a través de una llamada de función anónima:promises.push( function(item,i) { return new Promise(function(resolve, reject) { return block.apply(this,[item,index,resolve,reject]); }); }(item,index))
Entonces todas las promesas se resolverán:
return Promise.all(promises);
fuente
return block(item,index,resolve,reject);
lugar dereturn block.apply(this,[item,index,resolve,reject]);
, creo que loapply
hace más difícil de entender? ¿Hay algún beneficio del que no tenga conocimiento?Para todos los ejemplos a continuación, debe importar fs y módulos de ruta :
const fs = require('fs'); const path = require('path');
Leer archivos de forma asincrónica
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); });
Leer archivos de forma sincrónica, almacenar en matriz, clasificación 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/');
Leer archivos asincrónicos usando promesa
Más información sobre promisificar en este artículo .
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: regrese
undefined
para carpetas, si lo desea, puede filtrarlas:readFiles('absolute/path/to/directory/') .then(files => files.filter(file => file !== undefined)) .catch(err => console.log(err));
fuente
¿Eres una persona perezosa como yo y te encanta el módulo npm : D? Entonces mira esto.
npm install node-dir
ejemplo para leer archivos:
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 });
fuente
Si tiene Node.js 8 o posterior, puede usar el nuevo util.promisify. (Estoy marcando como opcionales las partes del código que tienen que ver con reformatear como un objeto, que solicitó la publicación original).
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); });
fuente
Otra versión con el método moderno de Promise. Es más corto que las otras respuestas basadas en 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));
fuente
Para que el código funcione sin problemas en diferentes entornos , path.resolve se puede usar en lugares donde se manipula path. Aquí hay un código que funciona mejor.
Parte de lectura:
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); }); }); }); }
Almacenar parte:
var data = {}; readFiles(path.resolve(__dirname, 'dirname/'), function(filename, content) { data[filename] = content; }, function(error) { throw err; });
fuente
Acabo de escribir esto y me parece más limpio:
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 }); };
Puede modificar el
response
formato según sus necesidades. Elresponse
formato de este código se verá así:{ "filename-01":{ "content":"This is the sample content of the file" }, "filename-02":{ "content":"This is the sample content of the file" } }
fuente
async / await
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) }
¿Quién quiere probar con generadores?
fuente