2012-04-06 7 views
37

Nie wiem, czy jest to możliwe, ale tutaj jest. A praca z callbackami czyni to jeszcze trudniejszym.Czytanie wszystkich plików w katalogu, przechowywanie ich w obiektach i wysyłanie obiektu

Mam katalog z plikami html, które chcę wysłać do klienta w porcjach Object z node.js i socket.io.

Wszystkie moje pliki są w/tmpl

Więc gniazdo musi przeczytać wszystkie pliki w/tmpl.

dla każdego pliku, który ma przechowywać dane w obiekcie o nazwie pliku jako kluczu, a treść jako wartość.

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

Ostateczny zwrotny jest również błędny. Musi zostać wywołany, gdy wszystkie pliki w katalogu zostaną wykonane.

Ale nie wiem, jak utworzyć kod, czy ktoś wie, czy to możliwe?

+3

Jeśli synchroniczny dostęp jest ok, można pominąć obsługi zdarzeń przy użyciu (blokowanie) metod 'readfileSync' i' readdirSync'. http://nodejs.org/docs/v0.4.8/api/fs.html#fs.readdirSync – rjz

+0

Ok, nie wiedziałem o readdir, który może być pomocny. I jakie są wady blokowania. Myślałem, że cały punkt node.js był taki, że nie był blokowany? Dlaczego wszyscy możemy nagle zablokować. –

+0

W przypadku asynchronicznych wywołań zwrotnych należy przeczytać: http://stackoverflow.com/questions/18983138/callback-after-all-asynchronous-foreach-allbacks-are-completed Istnieje wiele błędnych odpowiedzi, ale niektóre są poprawne. Jeden z nich używa liczników. – Vanuan

Odpowiedz

88

Istnieją trzy części. Czytanie, przechowywanie i wysyłanie.

Oto część czytanie:

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

Oto przechowywania część:

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

wysyłająca część należy do Ciebie. Możesz wysłać je jeden po drugim lub po przeczytaniu ukończenia.

Jeśli chcesz wysłać pliki po zakończeniu czytania, użyj wersji synchronizacji funkcji fs lub użyj obietnic. Asynchroniczne wywołania zwrotne nie są dobrym stylem.

Dodatkowo zapytano o usunięcie przedłużenia. Powinieneś zadawać pytania jeden po drugim. Nikt nie napisze kompletnego rozwiązania tylko dla Ciebie.

+0

Dzięki, myślę, że wykorzystam to. Jedno, czy możesz wyjaśnić, co robi '0 === - c'. –

+0

Możesz napisać to jako dwie linie 'c -', a następnie 'if (c === 0)' to samo. Zmniejsza tylko 'c' o' 1' i sprawdza czy osiągnął zero – stewe

+0

Ale zawsze będzie 0, czy nie? Dodajesz 1 w foreach i na tym samym foreach usuwasz 1, więc zawsze pozostaje 0, czy też się mylę? Czy to nie musi być 'if (c === files.length)', coś w tym stylu. –

1

Aby kod działał płynnie w różnych środowiskach, ścieżka.resolve może być używana w miejscach, w których manipuluje się ścieżką. Oto kod, który działa lepiej.

Czytając część:

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

Przechowywanie część:

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

Jak już zauważyłeś, jeszcze nie [Zarabiasz] (// meta.stackoverflow.com/q/146472/169503) wystarczająca [reputacja] (// stackoverflow.com/help/whats-reputation) na [komentarz do dowolnego posta ] (// stackoverflow.com/help/privileges/comment). Jednakże, proszę [nie nadużywaj przycisku * Post odpowiedz *] (// meta.stackoverflow.com/a/277942/2747593) z tego powodu. Zamiast tego [spędzić trochę czasu na stronie i zadawać pytania lub udzielać odpowiedzi] (// meta.stackexchange.com/a/188621/269535), a to da ci konieczny przedstawiciel. –

+0

Przyjęta odpowiedź @ScottWeldon ma problem. Nie jestem pewien, czy twój punkt widzenia jest ważny. Utrzymam tę odpowiedź, może pomóc innym. Nie zrozum mnie źle, ale nie potrzebuję reputacji ani punktów. :) – rsa

10

Jest to nowoczesny Promise wersja poprzedniego, stosując podejście Promise.all rozwiązać wszystkie obietnice, gdy wszystkie pliki zostały przeczytać:

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

Jak to Use:

tak proste, jak robi:

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

Przypuszcza się, że masz inną listę folderów można również iteracyjne nad tej listy, ponieważ wewnętrznej obietnicy.wszystko rozwiąże każdego następnie asynchronicznie:

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

Jak to działa

promiseAll robi magię. Zajmuje on blok funkcyjny podpisu function(item,index,resolve,reject), gdzie item jest bieżącym elementem w tablicy, index jego pozycją w tablicy, oraz resolve i reject funkcją wywołania zwrotnego. Każda obietnica zostanie wciśnięty w tablicy w bieżącym index i aktualnym item jako argumenty przez anonimowego wywołania funkcji:

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

Wtedy wszystkie obietnice zostaną rozwiązane:

return Promise.all(promises); 
0

Kolejna wersja z Nowoczesna metoda obietnicy. Jest krótsza, że ​​inne reakcje oparte na obietnicy

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

Czy jesteś leniwy człowiek jak ja i kochać moduł npm: D wtedy to sprawdzić.

npm zainstalować node-dir

Przykład odczytu plików:

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

Jeśli masz node.js 8 lub nowszego, można korzystać z nowej util.promisify. (Mam oznaczenie jako opcjonalne części kodu, które mają do czynienia z formatowania jako obiekt, którego oryginalny post wezwanego.)

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); 
    }); 
Powiązane problemy