2012-04-06 2 views
37

Я не знаю, возможно ли это, но здесь. И работа с обратными вызовами делает его еще более сложным.Чтение всех файлов в каталоге, сохранение их в объектах и ​​отправка объекта

У меня есть каталог с html-файлами, который я хочу отправить обратно клиенту в Object Chunk с node.js и socket.io.

Все мои файлы находятся в/TMPL

Так сокет должен прочитать все файлы в/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}); 

Последний обратный вызов также является неправильным. Он должен вызываться, когда все файлы в каталоге выполнены.

Но я не знаю, как создать код, кто-нибудь знает, возможно ли это?

+3

Если синхронный доступ в порядке, вы можете пропустить обработчик события, используя (блокирование) 'readfileSync' и' readdirSync'. http://nodejs.org/docs/v0.4.8/api/fs.html#fs.readdirSync – rjz

+0

Хорошо, я не знал о readdir, который может быть полезен. И каковы недостатки блокировки. Я думал, что весь узел node.js состоит в том, что он не блокирует? Почему мы можем внезапно блокировать. –

+0

Для асинхронных обратных вызовов прочтите следующее: http://stackoverflow.com/questions/18983138/callback-after-all-asynchronous-foreach-callbacks-are-completed Существует много неправильных ответов, но некоторые из них верны. Один из них использует счетчики. – Vanuan

ответ

88

Итак, есть три части. Чтение, хранение и отправка.

Вот читающая часть:

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, либо использовать обещания. Асинхронные обратные вызовы не являются хорошим стилем.

Кроме того, вы спрашивали об отмене расширения. Вы должны задавать вопросы один за другим. Никто не напишет полное решение только для вас.

+0

Спасибо, я думаю, что буду использовать это. Одна вещь, вы можете объяснить, что такое '0 === - c'. –

+0

Вы можете написать это как две строки 'c -', а затем 'if (c === 0)' то же самое. Он просто уменьшает 'c' на' 1' и проверяет, достиг ли он нуля – stewe

+0

Но оно всегда будет 0, или нет? Вы добавляете 1 в foreach, и в том же foreach вы удаляете 1, поэтому он всегда остается 0, или я ошибаюсь? Не нужно ли проверять 'if (c === files.length)', что-то вроде этого. –

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; 
}); 
+0

Как вы отметили, вы еще не [заработали] (// meta.stackoverflow.com/q/146472/169503) достаточно [репутации] (// stackoverflow.com/help/whats-reputation), чтобы [прокомментировать любую запись ] (// stackoverflow.com/help/privileges/comment). Однако, пожалуйста, [не злоупотребляйте кнопкой * ответ на сообщение] (// meta.stackoverflow.com/a/277942/2747593) из-за этого. Скорее, [потратьте некоторое время на сайт и задайте вопросы или дайте ответы] (// meta.stackexchange.com/a/188621/269535), и это даст вам необходимую вам информацию. –

+0

@ScottWeldon принятый ответ есть проблема. Я не уверен, что ваша точка зрения действительна. Я сохраню этот ответ, это может помочь другим. Не поймите меня неправильно, но мне не нужна репутация или очки. :) – rsa

10

Это современная 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); 
}); 

предположить, что у вас есть еще один список папок вы также можете пройти по этому списку, так как внутреннее обещание.все будет решать каждый из затем асинхронно:

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. Каждое обещание будет выталкиваться в массиве при текущем 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); 
0

Другая версия с Современный метод 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)); 
0

Вы ленивый человек, как я и люблю модуль НПМ: D затем проверить это.

НПМ установки 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 
    });  
0

Если у вас есть 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); 
    }); 
Смежные вопросы