¿Cómo adjuntar a un archivo en Node?


505

Estoy tratando de agregar una cadena a un archivo de registro. Sin embargo, writeFile borrará el contenido cada vez antes de escribir la cadena.

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

¿Alguna idea de cómo hacer esto de la manera fácil?

Respuestas:


799

Para anexos ocasionales, puede usar appendFile, lo que crea un nuevo identificador de archivo cada vez que se llama:

Asincrónicamente :

const fs = require('fs');

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

Sincrónicamente :

const fs = require('fs');

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

Pero si agrega repetidamente al mismo archivo, es mucho mejor reutilizar el identificador de archivo .


66
Desde el Nodo v0.8.0
denysonique

59
¿Alguien sabe si fs.appendFile mantiene abierto un enlace al archivo para que los anexos sean más rápidos? (en lugar de abrir / cerrar cada escritura) nodejs.org/api/…
nelsonic

77
En caso de que sea útil: tenga en cuenta que esto es asíncrono. Esto puede resultar en un momento extraño y otras cosas. Ej: si tiene process.exit()justo después fs.appendFile, puede salir antes de que se envíe la salida. (Usar returnestá bien.)
SilentSteel

66
Peor de los casos, puede utilizar la versión sincrónica, appendFileSync. nodejs.org/api/… Pero puede perder uno de los grandes beneficios de Node, que son las operaciones asíncronas. Asegúrate de atrapar los errores. Quizás en algunos sistemas operativos, puede obtener acceso denegado si solicita el identificador de archivo al mismo tiempo. No estoy seguro de eso.
SilentSteel

55
@chrisdew Gracias por la actualización ... pero ... si no vamos a usar la respuesta aceptada aquí, ¿qué se supone que debemos hacer? ¿Cómo resolviste este dilema?
zipzit

215

Cuando desee escribir en un archivo de registro, es decir, agregar datos al final de un archivo, nunca lo use appendFile. appendFileabre un identificador de archivo para cada pieza de datos que agrega a su archivo, después de un tiempo obtiene un hermoso EMFILEerror.

Puedo agregar que appendFileno es más fácil de usar que a WriteStream.

Ejemplo con 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());

Hasta 8000 en mi computadora, puede agregar datos al archivo, luego obtiene esto:

{ 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' }

Además, appendFileescribirá cuando esté habilitado, por lo que sus registros no se escribirán con marca de tiempo. Puede probar con el ejemplo, establecer 1000 en lugar de 100000, el orden será aleatorio, depende del acceso al archivo.

Si desea agregar a un archivo, debe usar una secuencia de escritura como esta:

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

Lo terminas cuando quieres. Ni siquiera está obligado a usar stream.end(), la opción predeterminada es AutoClose:true, por lo que su archivo finalizará cuando finalice su proceso y evite abrir demasiados archivos.


2
Esto es perfecto.
Pogrindis

3
Gracias por la excelente respuesta, pero mi duda es que, debido a la naturaleza asincrónica de Javascript, se ejecutará stream.end()antes que stream.write(), por lo que no deberíamos usar stream.end(), también, como mencionaste, AutoClose:Truees una opción predeterminada, ¿por qué molestarse en escribir una línea que no es válida? utilizar.
Aashish Kumar

13
due to asynchronous nature of Javascript... ¿Qué? Array.forEach es una operación sincrónica. JS es sincrónico. Simplemente proporciona algunas formas de administrar operaciones asincrónicas, como Promesas y async / wait.
Sharcoux

1
Supongo que da como fs.appendFileresultado demasiados archivos abiertos porque lo ejecuta de manera asincrónica (solo está creando asincrónicamente el identificador de archivos 10000), creo appendFileSyncque no tendría un problema similar, tampoco fs.appendFilecon el intervalo adecuado (1s es probablemente más que suficiente) o haciendo cola.
manzana manzana

1
@RedwolfPrograms Para el registro del servidor ocupado, tal vez sea cierto. Para un registro de ejecución por única vez, tal vez no. De todos modos, solo afirmo que el punto (al menos la razón) en esta respuesta no es correcto.
manzana manzana

122

Su código usando createWriteStream crea un descriptor de archivo para cada escritura. log.end es mejor porque le pide al nodo que se cierre inmediatamente después de la escritura.

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

66
falta la primera línea! debe ser 'var fs = require (' fs ');'
Stormbytes

55
O tal vez incluso mejor var fs = require('graceful-fs'), que resolvió algunos problemas conocidos. Vea los documentos para más información.
Marko Bonaci

3
Sin embargo, tanto la línea inicial como la final están en la misma línea :-p
binki

55
Tenga en cuenta : si está usando fs.createWriteStream, entonces use flags. Si está utilizando, fs.writeFileentonces es flag. Consulte Nodo JS Docs - Sistema de archivos para obtener más información.
Anish Nair

2
¡Ten cuidado! El parámetro no es "flags" sino "flag" (singular): nodejs.org/api/…
Benny Neugebauer

25

Además appendFile, también puede pasar una bandera writeFilepara agregar datos a un archivo existente.

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

Al pasar la marca 'a', los datos se agregarán al final del archivo.


3
Tenga en cuenta : si está usando fs.createWriteStream, entonces use flags. Si está utilizando, fs.writeFileentonces es flag. Consulte Nodo JS Docs - Sistema de archivos para obtener más información.
Anish Nair

19

Debe abrirlo y luego escribirle.

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

Aquí hay algunos enlaces que ayudarán a explicar los parámetros.

abrir
escribir
cerrar


EDITAR : Esta respuesta ya no es válida, busque el nuevo método fs.appendFile para agregar .


1
parece que Supercobra escribe constantemente el registro en el archivo de registro, no se recomienda el uso de fs.write en este caso, use fs.createWriteStream en su lugar. Lea nodejs.org/docs/v0.4.8/api/all.html#fs.write
angry kiwi el

10
La respuesta ya no es precisa a partir de nodejs v0.4.10.
Ruben Tan

debería ser '0666' en lugar de 666.
Ya Zhuang

13

Node.js 0.8 tiene fs.appendFile:

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

Documentación


3
fd = fs.openSync(path.join(process.cwd(), 'log.txt'), 'a')
fs.writeSync(fd, 'contents to append')
fs.closeSync(fd)

55
cualquier cosa sync () es casi siempre una mala idea a menos que esté 100% seguro de que NECESITA absolutamente. Incluso entonces, probablemente lo estás haciendo mal.
Zane Claes

44
No significa que esté mal. Simplemente lo hace sincrónicamente. Puede que no sea la mejor práctica para Node.js, pero es compatible.
Luis R.

2
Estaba usando "ur doin it wrong" en el sentido coloquial de meme de internet de la frase. Obviamente es compatible = P
Zane Claes

77
Se acordó la sincronización, pero a veces si solo está escribiendo un script interactivo, la sincronización está bien.
bryanmac

77
Escribir sincrónicamente está absolutamente bien si está utilizando la aplicación de línea de comandos de un solo usuario (por ejemplo, script para hacer algunas cosas). De esa manera es más rápido hacer cosas. ¿Por qué el nodo tendría métodos de sincronización si no fuera por este propósito?
Jan Święcki

3

Si desea una forma fácil y sin estrés de escribir registros línea por línea en un archivo, le recomiendo 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');

Probado con Nodo v8.9.4.


2

Mi enfoque es bastante especial. Básicamente uso la WriteStreamsolución pero sin realmente 'cerrar' el fd usando stream.end(). En cambio uso cork/ uncork. Esto obtuvo el beneficio del uso bajo de RAM (si eso es importante para alguien) y creo que es más seguro usarlo para iniciar sesión / grabar (mi caso de uso original).

El siguiente es un ejemplo bastante simple. Tenga en cuenta que acabo de agregar un pseudo forbucle para mostrar: en el código de producción estoy esperando los mensajes de websocket.

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

uncork vaciará los datos al archivo en el siguiente tic.

En mi escenario, hay picos de hasta ~ 200 escrituras por segundo en varios tamaños. Durante la noche, sin embargo, solo se necesitan unas pocas escrituras por minuto. El código funciona súper confiable incluso durante las horas pico.


1

Ofrezco esta sugerencia solo porque el control sobre las marcas abiertas a veces es útil, por ejemplo, es posible que desee truncar primero un archivo existente y luego agregarle una serie de escrituras, en cuyo caso use la bandera 'w' al abrir el archivo y no lo cierre hasta que todas las escrituras estén terminadas. Por supuesto, appendFile puede ser lo que buscas :-)

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


1

Usar fs.appendFileo fsPromises.appendFileson las opciones más rápidas y sólidas cuando necesita agregar algo a un archivo.

A diferencia de algunas de las respuestas sugeridas, si la ruta del archivo se proporciona a la appendFilefunción, en realidad se cierra sola . Solo cuando pasa un identificador de archivo que pasa por algo así como fs.open()tiene que encargarse de cerrarlo.

Lo probé con más de 50,000 líneas en un archivo.

Ejemplos:

(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.'
  );
})();

ingrese la descripción de la imagen aquí

Ref: https://github.com/nodejs/node/issues/7560
Ejecución de ejemplo: https://github.com/apickjs/apickFS/blob/master/writeLines.js


0

Aquí hay un guión completo. ¡Complete sus nombres de archivo y ejecútelo y debería funcionar! Aquí hay un video tutorial sobre la lógica detrás del guión.

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

0

una manera más fácil de hacer esto es

const fs = require('fs');
fs.appendFileSync('file.txt', 'message to append into file');

¿Más fácil que qué? ¿Ha marcado la respuesta principal, que ofrece exactamente la misma appendFileSyncsolución?
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');
}

0

Además de la respuesta de denysonique , a veces appendFilese utilizan tipos asíncronos y otros métodos asíncronos en NodeJS donde se devuelve la promesa en lugar de pasar la devolución de llamada. Para hacerlo, debe ajustar la función con promisifyHOF o importar funciones asíncronas desde el espacio de nombres de promesas:

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

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

Espero que ayude 😉

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.