Jak dołączyć do pliku w węźle?

505

Próbuję dołączyć ciąg do pliku dziennika. Jednak writeFile usunie zawartość za każdym razem przed zapisaniem ciągu.

fs.writeFile('log.txt', 'Hello Node', function (err) {
  if (err) throw err;
  console.log('It\'s saved!');
}); // => message.txt erased, contains only 'Hello Node'

Masz pomysł, jak to zrobić w prosty sposób?

supercobra
źródło

Odpowiedzi:

798

W przypadku sporadycznych dodatków możesz użyć appendFile, który tworzy nowy uchwyt pliku za każdym razem, gdy się nazywa:

Asynchronicznie :

const fs = require('fs');

fs.appendFile('message.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('Saved!');
});

Synchronicznie :

const fs = require('fs');

fs.appendFileSync('message.txt', 'data to append');

Ale jeśli wielokrotnie dołączasz do tego samego pliku, znacznie lepiej jest ponownie użyć uchwytu pliku .

denysonique
źródło
6
Od Node v0.8.0
denysonique
59
Czy ktoś wie, czy fs.appendFile utrzymuje link do otwartego pliku, więc dołączanie jest szybsze? (zamiast otwierać / zamykać każdy zapis) nodejs.org/api/…
nelsonic
7
Jeśli jest to przydatne: pamiętaj, że jest to asynchronizacja. Może to spowodować dziwne wyczucie czasu i inne rzeczy. Np .: jeśli masz process.exit()zaraz po tym fs.appendFile, możesz wyjść przed wysłaniem danych wyjściowych. (Używanie returnjest w porządku.)
SilentSteel
6
Gorszy przypadek, można użyć wersji synchronicznej, appendFileSync. nodejs.org/api/… Ale możesz stracić jedną z wielkich zalet Node, jaką są operacje asynchroniczne. Upewnij się, że łapiesz błędy. Być może w niektórych systemach operacyjnych możesz odmówić dostępu, jeśli jednocześnie żądasz uchwytu pliku. Nie jestem tego pewien.
SilentSteel,
5
@chrisdew Dzięki za aktualizację ... ale ... jeśli nie mamy tutaj użyć przyjętej odpowiedzi, co powinniśmy zrobić? Jak rozwiązałeś ten dylemat?
zipzit,
214

Kiedy chcesz zapisywać w pliku dziennika, tj. Dołączać dane na końcu pliku, nigdy nie używaj appendFile. appendFileotwiera uchwyt pliku dla każdego elementu danych, który dodajesz do pliku, po chwili pojawia się piękny EMFILEbłąd.

Mogę dodać, że appendFilenie jest łatwiejszy w użyciu niż WriteStream.

Przykład z appendFile:

console.log(new Date().toISOString());
[...Array(10000)].forEach( function (item,index) {
    fs.appendFile("append.txt", index+ "\n", function (err) {
        if (err) console.log(err);
    });
});
console.log(new Date().toISOString());

Do 8000 na moim komputerze możesz dołączyć dane do pliku, a następnie uzyskasz:

{ Error: EMFILE: too many open files, open 'C:\mypath\append.txt'
    at Error (native)
  errno: -4066,
  code: 'EMFILE',
  syscall: 'open',
  path: 'C:\\mypath\\append.txt' }

Co więcej, appendFile zapisze, gdy jest włączony, więc dzienniki nie będą zapisywane według znacznika czasu. Możesz przetestować na przykład, ustawić 1000 zamiast 100000, kolejność będzie losowa, zależy od dostępu do pliku.

Jeśli chcesz dołączyć do pliku, musisz użyć zapisywalnego strumienia w następujący sposób:

var stream = fs.createWriteStream("append.txt", {flags:'a'});
console.log(new Date().toISOString());
[...Array(10000)].forEach( function (item,index) {
    stream.write(index + "\n");
});
console.log(new Date().toISOString());
stream.end();

Kończysz to, kiedy chcesz. Nie musisz nawet używać tej stream.end()opcji, domyślną opcją jest AutoClose:true, więc plik zakończy się po zakończeniu procesu i unikniesz otwierania zbyt wielu plików.

Plaute
źródło
2
To jest na miejscu.
Pogrindis
3
Dzięki za świetną odpowiedź, ale wątpię, że ze względu na asynchroniczną naturę JavaScript, uruchomi się on stream.end()przed stream.write(), więc nie powinniśmy używać stream.end(), również, jak wspomniałeś, AutoClose:Truejest to opcja domyślna, więc po co zawracać sobie głowę pisaniem linii, która nie ma znaczenia posługiwać się.
Aashish Kumar
13
due to asynchronous nature of Javascript... Co? Array.forEach to operacja synchroniczna. JS jest synchroniczny. Zdarza się, że zapewniają pewne sposoby zarządzania operacjami asynchronicznymi, takie jak obietnice i asynchronizacja / oczekiwanie.
Sharcoux
1
Sądzę, że fs.appendFilespowoduje to zbyt wiele otwartych plików, ponieważ wykonujesz je w sposób asynchroniczny (po prostu asynchronicznie tworzysz uchwyt pliku 10000), uważam, appendFileSyncże nie miałby podobnego problemu, również fs.appendFilez niewłaściwym interwałem (1s jest prawdopodobnie więcej niż wystarczające) lub w kolejce.
jabłko jabłko
1
@RedwolfPrograms W przypadku zajętego dziennika serwera może być to prawda. Na jednorazowy dziennik wykonania może nie. W każdym razie po prostu stwierdzam, że punkt (przynajmniej powód) w tej odpowiedzi jest nieprawidłowy.
jabłko jabłko
122

Twój kod za pomocą createWriteStream tworzy deskryptor pliku dla każdego zapisu. log.end jest lepszy, ponieważ prosi węzeł o zamknięcie natychmiast po zapisie.

var fs = require('fs');
var logStream = fs.createWriteStream('log.txt', {flags: 'a'});
// use {flags: 'a'} to append and {flags: 'w'} to erase and write a new file
logStream.write('Initial line...');
logStream.end('this is the end line');
Fabio Ferrari
źródło
6
brakuje pierwszej linii! powinno być „var fs = wymagana („ fs ”);”
Stormbytes,
5
A może nawet lepiej var fs = require('graceful-fs'), co rozwiało niektóre znane problemy. Zobacz dokumentację, aby uzyskać więcej informacji.
Marko Bonaci
3
Zarówno linia początkowa, jak i końcowa znajdują się na tej samej linii :-p
binki
5
Uwaga : jeśli używasz fs.createWriteStream, użyj flags. Jeśli używasz, fs.writeFileto jest flag. Więcej informacji można znaleźć w dokumencie Węzły Dokumenty JS - System plików .
Anish Nair,
2
Bądź ostrożny! Ten parametr to nie „flagi”, ale „flaga” (liczba pojedyncza): nodejs.org/api/…
Benny Neugebauer
25

Poza tym appendFilemożesz również przekazać flagę, writeFileaby dołączyć dane do istniejącego pliku.

fs.writeFile('log.txt', 'Hello Node',  {'flag':'a'},  function(err) {
    if (err) {
        return console.error(err);
    }
});

Przekazywanie flagi „a” spowoduje dołączenie danych na końcu pliku.

AJ 9
źródło
3
Uwaga : jeśli używasz fs.createWriteStream, użyj flags. Jeśli używasz, fs.writeFileto jest flag. Więcej informacji można znaleźć w dokumencie Węzły Dokumenty JS - System plików .
Anish Nair,
19

Musisz go otworzyć, a następnie napisać do niego.

var fs = require('fs'), str = 'string to append to file';
fs.open('filepath', 'a', 666, function( e, id ) {
  fs.write( id, 'string to append to file', null, 'utf8', function(){
    fs.close(id, function(){
      console.log('file closed');
    });
  });
});

Oto kilka linków, które pomogą wyjaśnić parametry

otwórz
napisz
zamknij


EDYCJA : Ta odpowiedź nie jest już poprawna, spójrz na nową metodę fs.appendFile do dołączania.

Corey Hart
źródło
1
wygląda na to, że supercobra stale zapisuje dziennik w pliku dziennika, w tym przypadku nie zaleca się używania fs.write, zamiast tego należy użyć fs.createWriteStream. Przeczytaj nodejs.org/docs/v0.4.8/api/all.html#fs.write
wściekły kiwi
10
Odpowiedź nie jest już dokładna od nodejs v0.4.10.
Ruben Tan,
powinna to być „0666”, a nie 666.
Ya Zhuang
13

Node.js 0.8 ma fs.appendFile:

fs.appendFile('message.txt', 'data to append', (err) => {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

Dokumentacja

chbrown
źródło
3
fd = fs.openSync(path.join(process.cwd(), 'log.txt'), 'a')
fs.writeSync(fd, 'contents to append')
fs.closeSync(fd)
Luis R.
źródło
5
Sync () jest prawie zawsze złym pomysłem, chyba że jesteś w 100% pewien, że absolutnie POTRZEBUJESZ. Nawet wtedy prawdopodobnie robisz to źle.
Zane Claes
4
To nie znaczy, że to źle. Po prostu robi to synchronicznie. Może nie być najlepszą praktyką dla Node.js, ale jest obsługiwany.
Luis R.
2
Użyłem słowa „ur robi to źle” w potocznym znaczeniu tego zdania w języku memów internetowych. Oczywiście jest obsługiwany = P
Zane Claes
7
Zgoda na asynchronizację, ale czasami, jeśli piszesz tylko interaktywny skrypt, synchronizacja jest w porządku.
bryanmac
7
Pisanie synchroniczne jest absolutnie w porządku, jeśli wykonujesz aplikację z jednym wierszem poleceń (np. Skrypt do robienia pewnych rzeczy). W ten sposób robienie rzeczy jest szybsze. Dlaczego węzeł miałby mieć metody synchronizacji, gdyby nie do tego celu?
Jan Święcki
3

Jeśli chcesz w łatwy i bezstresowy sposób zapisywać logi linia po linii w pliku, polecam fs-extra :

const os = require('os');
const fs = require('fs-extra');

const file = 'logfile.txt';
const options = {flag: 'a'};

async function writeToFile(text) {
  await fs.outputFile(file, `${text}${os.EOL}`, options);
}

writeToFile('First line');
writeToFile('Second line');
writeToFile('Third line');
writeToFile('Fourth line');
writeToFile('Fifth line');

Testowane z Node v8.9.4.

Benny Neugebauer
źródło
2

Moje podejście jest raczej wyjątkowe. Zasadniczo korzystam z WriteStreamrozwiązania, ale bez „zamykania” fd za pomocą stream.end(). Zamiast tego używam cork/uncork . Zaletą tego jest niskie zużycie pamięci RAM (jeśli ma to znaczenie dla każdego) i uważam, że bezpieczniej jest używać go do logowania / nagrywania (mój oryginalny przypadek użycia).

Poniżej znajduje się dość prosty przykład. Zauważ, że właśnie dodałem pseudo- forpętlę do prezentacji - w kodzie produkcyjnym czekam na wiadomości websocket.

var stream = fs.createWriteStream("log.txt", {flags:'a'});
for(true) {
  stream.cork();
  stream.write("some content to log");
  process.nextTick(() => stream.uncork());
}

uncork opróżni dane do pliku w następnym tiku.

W moim scenariuszu występują szczyty do ~ 200 zapisów na sekundę w różnych rozmiarach. W nocy potrzeba jednak tylko kilku zapisów na minutę. Kod działa bardzo niezawodnie nawet w godzinach szczytu.

Tom-Oliver Heidel
źródło
1

Oferuję tę sugestię tylko dlatego, że kontrola nad otwartymi flagami jest czasami przydatna, na przykład możesz najpierw obciąć istniejący plik, a następnie dołączyć do niego serię zapisów - w takim przypadku użyj flagi „w” podczas otwierania pliku i nie zamykaj go, dopóki wszystkie zapisy nie zostaną wykonane. Oczywiście appendFile może być tym, czego szukasz :-)

  fs.open('log.txt', 'a', function(err, log) {
    if (err) throw err;
    fs.writeFile(log, 'Hello Node', function (err) {
      if (err) throw err;
      fs.close(log, function(err) {
        if (err) throw err;
        console.log('It\'s saved!');
      });
    });
  });
balrob
źródło
1

Korzystanie z pakietu jfile :

myFile.text+='\nThis is new line to be appended'; //myFile=new JFile(path);
Abdennour TOUMI
źródło
1

Używanie fs.appendFilelub fsPromises.appendFilejest najszybszymi i najbardziej niezawodnymi opcjami, gdy trzeba dołączyć coś do pliku.

W przeciwieństwie do niektórych sugerowanych odpowiedzi, jeśli ścieżka do pliku jest dostarczona do appendFilefunkcji, faktycznie się zamyka . Dopiero po przejściu do uchwytu pliku, który dostajesz przez coś takiego fs.open(), musisz zadbać o jego zamknięcie.

Próbowałem z ponad 50 000 wierszy w pliku.

Przykłady:

(async () => {
  // using appendFile.
  const fsp = require('fs').promises;
  await fsp.appendFile(
    '/path/to/file', '\r\nHello world.'
  );

  // using apickfs; handles error and edge cases better.
  const apickFileStorage = require('apickfs');
  await apickFileStorage.writeLines(
    '/path/to/directory/', 'filename', 'Hello world.'
  );
})();

wprowadź opis zdjęcia tutaj

Ref: https://github.com/nodejs/node/issues/7560
Przykład wykonania: https://github.com/apickjs/apickFS/blob/master/writeLines.js

vivek agarwal
źródło
0

Oto pełny skrypt. Wpisz nazwy swoich plików i uruchom je, a powinno działać! Oto samouczek wideo na temat logiki skryptu.

var fs = require('fs');

function ReadAppend(file, appendFile){
  fs.readFile(appendFile, function (err, data) {
    if (err) throw err;
    console.log('File was read');

    fs.appendFile(file, data, function (err) {
      if (err) throw err;
      console.log('The "data to append" was appended to file!');

    });
  });
}
// edit this with your file names
file = 'name_of_main_file.csv';
appendFile = 'name_of_second_file_to_combine.csv';
ReadAppend(file, appendFile);
Jvieitez
źródło
0

łatwiej to zrobić

const fs = require('fs');
fs.appendFileSync('file.txt', 'message to append into file');
Adeojo Emmanuel IMM
źródło
Łatwiej niż co? Czy sprawdziłeś najwyższą odpowiedź, która daje dokładnie to samo appendFileSyncrozwiązanie?
Dan Dascalescu
0
const inovioLogger = (logger = "") => {
    const log_file = fs.createWriteStream(__dirname + `/../../inoviopay-${new Date().toISOString().slice(0, 10)}.log`, { flags: 'a' });
    const log_stdout = process.stdout;
    log_file.write(logger + '\n');
}
sunilsingh
źródło
0

Oprócz odpowiedzi denysonique , czasem appendFilew NodeJS stosowane są czasem metody asynchroniczne i inne metody asynchroniczne, w których obietnica powraca zamiast przekazywania wywołania zwrotnego. Aby to zrobić, musisz owinąć funkcję promisifyHOF lub zaimportować funkcje asynchroniczne z przestrzeni nazw obietnic:

const { appendFile } = require('fs').promises;

await appendFile('path/to/file/to/append', dataToAppend, optionalOptions);

Mam nadzieję, że to pomoże 😉

Alex Gusev
źródło