source

디렉토리의 모든 파일을 읽고 개체에 저장한 후 개체 전송

ittop 2023. 9. 14. 23:34
반응형

디렉토리의 모든 파일을 읽고 개체에 저장한 후 개체 전송

이것이 가능한지는 모르겠지만, 여기 있습니다.또한 콜백을 사용하는 것은 훨씬 더 어려운 일입니다.

node.js와 socket.io 이 있는 Object chunk에 있는 클라이언트에 다시 보내고 싶은 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기능 또는 사용 약속.비동기식 콜백은 좋은 스타일이 아닙니다.

추가로 연장 철거에 관해 문의하셨습니다.질문을 하나씩 진행해야 합니다.아무도 당신만을 위한 완벽한 해결책을 쓰지 않을 것입니다.

아래의 모든 예에 대해 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/');

약속을 사용하여 파일 비동기화 읽기

약속에 대한 자세한 내용은 이 기사에서 확인할 수 있습니다.

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

이것은 현대적인 것입니다.Promise는전의를a을 사용하여 이전 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 );
});

내부 약속 이후로 이 목록에서 반복할 수 있는 다른 폴더 목록이 있다고 가정합니다.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)where, item의재다다재 입니다.index및치의및resolve그리고.rejectPromise콜백 함수입니다. Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ Δ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);

저처럼 게으른 사람이고 npm module :D를 사랑하는 사람인가요 그럼 확인해보세요.

npm 설치 노드-슬라이스

파일 읽기 예:

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

제가 방금 쓴 글인데 제가 보기엔 더 깨끗합니다.

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"
   }
}

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

Node.js 8 이후 버전이 있는 경우 새로운 util.promise를 사용할 수 있습니다. (원래 게시물에서 요청한 개체로 재포맷과 관련된 코드 부분을 선택사항으로 표시합니다.)

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

다양한 환경에서 코드가 원활하게 작동하려면 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;
});

비동기식/비동기식

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

발전기로 해보고 싶은 사람?

그래서, 만약 누군가가 초보자 친화적인 접근법을 찾고 있다면, 여기 제가 있습니다.

실제 해결에 앞서, 우리는 비동기적기능과 약속을 이해해야 합니다.비동기 함수는 단일 스레드의 범위 밖에서 실행되며, 주 스레드와 평행하게 실행될 수 있습니다.이것은 기본적으로 어떤 함수가 비동기식일 경우, 함수가 완료되기를 기다리는 대신 자바스크립트가 다음 행으로 이동한다는 것을 의미합니다.기능이 동시에 실행됩니다.

fs.readFile()은 비동기이므로 다음 행을 실행하고 병렬로 실행합니다.

이제 약속을 이해해 보겠습니다.약속은 기본적으로 비동기 함수의 성공 또는 실패를 반환하는 개체입니다.예:

//Defining Promise
const promise = new Promise((resolve,reject)=>{
    //This is an asynchronous function, which takes 2 seconds to execute
    setTimeout(()=>{
        if(1!=0){
            //If there is an error reject the promise
            reject(new Error("This is an error messahe"))
        } 
        else{
            //if there are no errors we resolve the promise
            resolve({'userId':'id'})
        }
    },2000)
})

자, 원래의 문제로 오는 것은

const fs = require("fs");

// Read the File Names.
function readFileNames() {
  // Defining a new promise
  return new Promise((resolve, reject) => {
    try {
      //read the directory
      fs.readdir("./public/", (err, files) => {
        // If read completes, resolve the promise.
        resolve(files);
      });
    } catch (err) {
      // If there is an error, reject the promise. 
      reject(err);
    }
  });
}

// Read content of a given file
function readFileContent(file) {
  return new Promise((resolve, reject) => {
    try {
      fs.readFile("./public/" + file, "utf8", (err, content) => {
        resolve(content);
      });
    } catch (err) {
      reject(err);
    }
  });
}

//sending the data
module.exports = {
// If we want to wait for a function to wait for a promise to be 
// resolved we define it as 'async'
  async get(req, res) {
    let data = {};
    //Here we wait for the promise to resolve, thus await is used
    const fileNames = await readFileNames();
    let count = 0;
    // Note we again use async since we want to wait for promise
    fileNames.forEach(async (name) => {
      // We wait for the content of file.
      const content = await readFileContent(name);
      data[name] = content;
      // Since we want to send data after the loop is completed.
      if (count === fileNames.length - 1) {
        res.send(data);
      }
      count++;
    });
  }

이 예제에서는 배열을 만듭니다.하지만 당신이 원한다면 객체를 만들 수 있습니다.

const fs = require('fs-extra')

const dirname = `${process.cwd()}/test`
const fileNames = await fs.readdir(dirname)

const files = []
for(const fileName of fileNames){
    const file = await fs.readFile(`${dirname}/${fileName}`, 'utf8')
    files.push(file)
}          

2022년이 되면서 더 깨끗한 네이티브 기능이 등장했습니다.

import fs from "fs/promises";

const readDirectory = async (dir) => {
  const files = await fs.readdir(dir);

  const values = files.flatMap(async (file) => {
    const filePath = path.join(dir, file);
    const stat = await fs.stat(filePath);
    if (stat.isDirectory()) {
      return;
    }
    return fs.readFile(filePath);
  });

  const buffers = await Promise.all(values);
  // Remove this line to keep the raw buffers
  const contents = buffers.filter(Boolean).map((l) => l.toString());
  return contents;
};

그리고 타이프스크립트 버전.

import fs from "fs/promises";

const readDirectory = async (dir: string) => {
  const files = await fs.readdir(dir);

  const values = files.flatMap(async (file) => {
    const filePath = path.join(dir, file);
    const stat = await fs.stat(filePath);
    if (stat.isDirectory()) {
      return;
    }
    return fs.readFile(filePath);
  }) as Array<Promise<Buffer>>;

  const buffers = await Promise.all(values);
  // Remove this line to keep the raw buffers
  const contents = buffers.filter(Boolean).map((l) => l.toString());
  return contents;
};

그리고 재귀적 버전.

import fs from "fs/promises";

const getRecursiveFileReads = async (dir: string): Promise<Array<Promise<Buffer>>> => {
  const files = await fs.readdir(dir);
  return files.flatMap(async (file) => {
    const filePath = path.join(dir, file);
    const stat = await fs.stat(filePath);
    if (stat.isDirectory()) {
      return getRecursiveFileReads(filePath);
    }
    return fs.readFile(filePath);
  }) as Array<Promise<Buffer>>;
};
const readDirectory = async (dir: string) => {
  const promises = await getRecursiveFileReads(dir);
  const buffers = await Promise.all(promises);
  return buffers;
};

언급URL : https://stackoverflow.com/questions/10049557/reading-all-files-in-a-directory-store-them-in-objects-and-send-the-object

반응형