[node.js] 디렉토리의 모든 파일을 읽고, 객체에 저장하고, 객체를 보냅니다.

이것이 가능한지 모르겠지만 여기에 있습니다. 그리고 콜백을 사용하면 훨씬 더 어려워집니다.

node.js 및 socket.io를 사용하여 개체 청크에서 클라이언트로 다시 보낼 html 파일이있는 디렉터리가 있습니다.

내 모든 파일은 / 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});

마지막 콜백도 잘못되었습니다. 디렉토리의 모든 파일이 완료되면 호출해야합니다.

하지만 코드를 만드는 방법을 모르겠습니다. 이것이 가능한지 아는 사람이 있습니까?



답변

그래서 세 부분이 있습니다. 읽기, 저장 및 보내기.

읽기 부분은 다음과 같습니다.

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함수를 사용하거나 promise를 사용해야합니다. 비동기 콜백은 좋은 스타일이 아닙니다.

또한 확장 프로그램 제거에 대해 질문했습니다. 질문을 하나씩 진행해야합니다. 아무도 당신만을위한 완전한 솔루션을 작성하지 않을 것입니다.


답변

이것은 모든 파일을 읽었을 때 모든 promise를 해결 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및 콜백 함수. 각 promise는 현재 배열에 푸시되고 현재 는 익명 함수 호출을 통해 인수로 사용됩니다.rejectPromiseindexitem

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);


답변

아래의 모든 예에서 fs경로 모듈 을 가져와야 합니다.

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/');

promise를 사용하여 비동기 파일 읽기

기사 에서 promisify 에 대한 추가 정보 .

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));


답변

당신은 나처럼 게으른 사람이고 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 
   });


답변

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


답변

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));


답변

코드가 다른 환경에서 원활 하게 작동하려면 경로가 조작되는 곳에서 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;
});