Bir dizindeki tüm dosyaları okumak, onları nesnelerde saklamak ve nesneyi göndermek


91

Bunun mümkün olup olmadığını bilmiyorum ama işte burada. Ve geri aramalarla çalışmak işi daha da zorlaştırır.

İstemciye node.js ve socket.io ile Object chunks içinde geri göndermek istediğim html dosyalarını içeren bir dizine sahibim.

Tüm dosyalarım / tmpl içinde

Bu yüzden soket / tmpl'deki tüm dosyaları okumalıdır.

her dosya için, verileri anahtar olarak dosya adı ve değer olarak içerik ile bir nesnede depolaması gerekir.

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

Son geri arama da yanlış. Dizindeki tüm dosyalar tamamlandığında çağrılması gerekir.

Ama kodu nasıl oluşturacağımı bilmiyorum, bunun mümkün olup olmadığını bilen var mı?


4
Eşzamanlı erişim uygunsa, (engelleme) readfileSyncve readdirSyncyöntemlerini kullanarak olay işleyicisini atlayabilirsiniz . nodejs.org/docs/v0.4.8/api/fs.html#fs.readdirSync
rjz

Tamam, readdir hakkında bir bilgim yoktu, bu yardımcı olabilir. Ve engellemenin olumsuz yönleri nelerdir? Node.js'nin tüm amacının engellememesi olduğunu sanıyordum. Neden birdenbire engel olalım ki?
Saif Bechan

Zaman uyumsuz geri aramalar için şunu okuyun: stackoverflow.com/questions/18983138/… Birçok yanlış yanıt var, ancak bazıları doğru. Bir tanesi sayaç kullanıyor.
Vanuan

Yanıtlar:


169

Yani, üç bölüm var. Okuma, saklama ve gönderme.

İşte okuma kısmı:

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

İşte depolama kısmı:

var data = {};
readFiles('dirname/', function(filename, content) {
  data[filename] = content;
}, function(err) {
  throw err;
});

Gönderme kısmı size kalmış. Bunları tek tek veya okuduktan sonra göndermek isteyebilirsiniz.

Okuma tamamlandıktan sonra dosya göndermek istiyorsanız, ya fsişlevlerin senkron sürümlerini ya da vaatleri kullanmalısınız. Zaman uyumsuz geri aramalar iyi bir stil değildir.

Ek olarak bir uzantının kaldırılmasını istediniz. Sorularla tek tek ilerlemelisiniz. Kimse size tam bir çözüm yazmayacak.


Teşekkürler, bunu kullanacağımı düşünüyorum. Tek bir şey, ne olduğunu açıklayabilir misin 0===--c?
Saif Bechan

1
Bunu iki satır olarak yazabilirsin c--ve sonra if (c===0)bu aynı. Bu sadece azaltır ctarafından 1sıfır ulaştıysa ve kontroller
Stewe

Ama her zaman 0 mı olacak yoksa olmayacak mı? Foreach'a 1 eklersiniz ve aynı foreach'ta 1'i kaldırırsınız, böylece her zaman 0 kalır, yoksa yanılıyor muyum? Çekin if(c===files.length)böyle bir şey olması gerekmiyor mu?
Saif Bechan

5
Zaman uyumsuz doğası nedeniyle foreach döngüsü büyük olasılıkla ilk readFilehtml döndürülmeden önce biter , bu nedenle hemen (dosya sayısına) ckadar gitmeli xve html diskten geldiğinde azalmalı (bu çok daha geç)
stewe

1
Oh, işte bu derin bir mantık, güzel bir tane. Düğüm hakkında öğrenecek çok şeyim var. Yardım için teşekkürler!
Saif Bechan

16

Bu, tüm dosyalar okunduğunda tüm vaatleri çözmek Promiseiçin bir Promise.allyaklaşım kullanan, öncekinin modern bir sürümüdür :

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

Bu nasıl kullanılır:

Yapmak kadar basit:

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

Diyelim ki, dahili promise.all her birini eşzamansız olarak çözeceğinden, bu listeyi yineleyebileceğiniz başka bir klasör listeniz olduğunu varsayalım:

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

Nasıl çalışır

promiseAllBüyü yapar. Bu imzanın bir fonksiyon bloğunu alır function(item,index,resolve,reject), itemcari dizide öğesi olan indexdizide konumu ve resolveve geri arama fonksiyonları. Her söz, anonim bir işlev çağrısı aracılığıyla geçerli ve geçerli argüman olarak bir dizi içinde itilecektir :rejectPromiseindexitem

promises.push( function(item,i) {
        return new Promise(function(resolve, reject) {
            return block.apply(this,[item,index,resolve,reject]);
        });
    }(item,index))

Sonra tüm sözler çözülecek:

return Promise.all(promises);

1
Harika kod Loreto, ama neden return block(item,index,resolve,reject);bunun yerine kullanmayalım return block.apply(this,[item,index,resolve,reject]);, sanırım applyanlamayı zorlaştırıyor - farkında olmadığım bir fayda var mı?
BOŞ işaretçi

1
@NULLpointer teşekkürler. Uygulamanın faydalarından biri, değişkenleri geçirmek için dizileri kullanabilmeniz ve daha fazlasını değişkenlerin self.apply (someObjContext, [arg1, arg2]) gibi tanımlandığı bağlamı geçirebilmenizdir. Bu özel durumda aslında buna ihtiyacınız yok, ancak bir kütüphanedeyseniz bu nesne bağlamı başka bir şey olabilir ...
loretoparisi

10

Aşağıdaki tüm örnekler için fs ve yol modüllerini içe aktarmanız gerekir :

const fs = require('fs');
const path = require('path');

Dosyaları eşzamansız olarak okuyun

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

Kullanım:

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

Dosyaları eşzamanlı olarak okuyun, dizide depolayın, doğal sıralama

/**
 * @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;
}

Kullanım:

// return an array list of objects
// each object represent a file
const files = readFilesSync('absolute/path/to/directory/');

Promise kullanarak dosyaları eşzamansız olarak okuyun

Bu makalede söz vermeyle ilgili daha fazla bilgi .

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

Kullanım:

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

Not:undefined Klasörler için geri dönün , isterseniz onları filtreleyebilirsiniz:

readFiles('absolute/path/to/directory/')
  .then(files => files.filter(file => file !== undefined))
  .catch(err => console.log(err));

5

Benim gibi tembel bir insan mısınız ve npm modülünü seviyor musunuz : D o zaman şuna bir bakın .

npm yükleme düğüm-dizin

dosyaları okumak için örnek:

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

4

Node.js 8 veya sonraki bir sürümüne sahipseniz, yeni util.promisify'ı kullanabilirsiniz. (Orijinal gönderinin talep ettiği, bir nesne olarak yeniden biçimlendirmeyle ilgili kod bölümlerini isteğe bağlı olarak işaretliyorum.)

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

3

Promise'ın modern yöntemiyle başka bir versiyon. Diğerlerinin Promise'a göre yanıt vermesi daha kısadır:

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

güzel ve net! Teşekkürler @Paul
Chaos Legion

1

Kodun farklı ortamlarda sorunsuz çalışması için path.resolve , yolun manipüle edildiği yerlerde kullanılabilir. İşte daha iyi çalışan kod.

Okuma bölümü:

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

Parça saklama:

var data = {};
readFiles(path.resolve(__dirname, 'dirname/'), function(filename, content) {
  data[filename] = content;
}, function(error) {
  throw err;
});

1

Bunu daha yeni yazdım ve bana daha temiz görünüyor:

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

responseFormatı ihtiyacınıza göre değiştirebilirsiniz . responseBu koddan biçimi gibi görünecektir:

{
   "filename-01":{
      "content":"This is the sample content of the file"
   },
   "filename-02":{
      "content":"This is the sample content of the file"
   }
}


0

eşzamansız / bekleyin

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

Jeneratörleri kim denemek ister?

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.