จะเพิ่มไฟล์ใน Node ได้อย่างไร?


505

ฉันพยายามที่จะผนวกสตริงลงในไฟล์บันทึก อย่างไรก็ตาม writeFile จะลบเนื้อหาในแต่ละครั้งก่อนที่จะเขียนสตริง

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

มีความคิดวิธีการทำเช่นนี้วิธีที่ง่าย?

คำตอบ:


799

สำหรับการผนวกเป็นครั้งคราวคุณสามารถใช้appendFileซึ่งจะสร้างการจัดการไฟล์ใหม่ทุกครั้งที่เรียกว่า:

แบบอะซิงโครนัส :

const fs = require('fs');

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

พร้อมกัน:

const fs = require('fs');

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

แต่ถ้าคุณต่อท้ายไฟล์เดียวกันซ้ำ ๆ จะเป็นการดีกว่ามากที่จะใช้ตัวจัดการไฟล์อีกครั้ง


6
ตั้งแต่โหนด v0.8.0
denysonique

59
ไม่มีใครรู้ว่าfs.appendFileเก็บลิงค์ไปยังไฟล์ที่เปิดอยู่เพื่อให้ผนวกเร็วขึ้นหรือไม่ (แทนที่จะเปิด / ปิดแต่ละการเขียน) nodejs.org/api/…
nelsonic

7
ในกรณีที่เป็นประโยชน์: โปรดทราบว่านี่เป็นแบบซิงค์ สิ่งนี้อาจส่งผลให้เกิดจังหวะแปลก ๆ และสิ่งอื่น ๆ เช่นถ้าคุณมีprocess.exit()หลังจากfs.appendFileคุณอาจออกก่อนที่จะส่งออก (การใช้งานreturnปกติ)
SilentSteel

6
กรณีที่เลวร้ายยิ่งคุณสามารถใช้รุ่นซิงโครนัส, appendFileSync. nodejs.org/api/…แต่คุณอาจสูญเสียหนึ่งในผลประโยชน์ที่ยอดเยี่ยมของ Node ซึ่งเป็นการดำเนินการแบบ async ตรวจสอบให้แน่ใจว่าคุณพบข้อผิดพลาด บางทีในบางระบบปฏิบัติการคุณสามารถรับการเข้าถึงถูกปฏิเสธหากร้องขอการจัดการไฟล์ในเวลาเดียวกัน ไม่แน่ใจเกี่ยวกับเรื่องนั้น
SilentSteel

5
@ chrisdew ขอบคุณสำหรับการอัปเดต .. แต่ ... หากเราไม่ใช้คำตอบที่ยอมรับได้ที่นี่เราควรจะทำอย่างไร? คุณแก้ปัญหาวิกฤตินี้อย่างไร?
zipzit

215

เมื่อคุณต้องการที่จะเขียนในล็อกไฟล์คือการผนวกข้อมูลไปยังจุดสิ้นสุดของแฟ้มที่ไม่เคยappendFileใช้งาน appendFileเปิดตัวจัดการไฟล์สำหรับข้อมูลแต่ละชิ้นที่คุณเพิ่มลงในไฟล์หลังจากผ่านไประยะหนึ่งคุณจะได้รับEMFILEข้อผิดพลาดที่สวยงาม

ฉันสามารถเพิ่มที่ไม่ได้ใช้งานง่ายกว่าappendFileWriteStream

ตัวอย่างด้วย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());

มากถึง 8000 บนคอมพิวเตอร์ของฉันคุณสามารถผนวกข้อมูลลงในไฟล์จากนั้นคุณจะได้รับสิ่งนี้:

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

นอกจากนี้appendFileจะเขียนเมื่อเปิดใช้งานดังนั้นบันทึกของคุณจะไม่ถูกเขียนโดยการประทับเวลา คุณสามารถทดสอบด้วยตัวอย่างตั้ง 1,000 แทน 100000 ลำดับจะสุ่มขึ้นอยู่กับการเข้าถึงไฟล์

หากคุณต้องการต่อท้ายไฟล์คุณต้องใช้สตรีมแบบเขียนได้ดังนี้:

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

คุณจบมันเมื่อคุณต้องการ คุณไม่จำเป็นต้องใช้แม้แต่stream.end()ตัวเลือกเริ่มต้นคือAutoClose:trueดังนั้นไฟล์ของคุณจะสิ้นสุดเมื่อกระบวนการของคุณสิ้นสุดลงและคุณหลีกเลี่ยงการเปิดไฟล์มากเกินไป


2
นี่คือจุดที่
Pogrindis

3
ขอบคุณสำหรับคำตอบที่ดี แต่ข้อสงสัยของฉันคือเนื่องจากลักษณะของ Javascript แบบอะซิงโครนัสมันจะทำงานstream.end()ก่อนstream.write()ดังนั้นเราจึงไม่ควรใช้stream.end()เช่นที่คุณกล่าวถึงว่าAutoClose:Trueเป็นตัวเลือกเริ่มต้นแล้วทำไมต้องเขียนบรรทัดที่ไม่มี ใช้.
Aashish Kumar

13
due to asynchronous nature of Javascript... อะไร? Array.forEach เป็นการดำเนินการแบบซิงโครนัส JS ซิงโครนัส มันเกิดขึ้นเพื่อให้วิธีการจัดการการดำเนินการแบบอะซิงโครนัสเช่นสัญญาและ async / รอ
Sharcoux

1
ฉันเดาfs.appendFileผลในไฟล์ที่เปิดมากเกินไปเพราะคุณดำเนินการในลักษณะตรงกัน (คุณเพียงแค่การถ่ายทอดสดการสร้าง 10000 การจัดการไฟล์) ผมเชื่อว่าappendFileSyncไม่น่าจะมีปัญหาที่คล้ายกันยังไม่ได้fs.appendFileมีช่วงเวลาที่เหมาะสม (1s อาจมากกว่าพอ) หรือเข้าคิว
apple apple

1
@RedwolfPrograms สำหรับบันทึกเซิร์ฟเวอร์ไม่ว่างอาจเป็นจริง สำหรับบันทึกการดำเนินการเพียงครั้งเดียวอาจไม่ใช่ อย่างไรก็ตามฉันเพียงแค่ระบุว่าจุด (อย่างน้อยเหตุผล) ในคำตอบนี้ไม่ถูกต้อง
apple apple

122

รหัสของคุณที่ใช้ createWriteStream จะสร้าง file descriptor สำหรับการเขียนทุกครั้ง log.end ดีกว่าเพราะขอให้โหนดปิดทันทีหลังจากเขียน

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

6
ไม่มีบรรทัดแรก! ควรเป็น 'var fs = require (' fs ');'
Stormbytes

5
หรืออาจจะดียิ่งขึ้นvar fs = require('graceful-fs')ซึ่งทำให้เข้าใจปัญหาบางอย่างได้ ดูเอกสารสำหรับข้อมูลเพิ่มเติม
Marko Bonaci

3
ทั้งบรรทัดเริ่มต้นและปลายทางอยู่ในบรรทัดเดียวกันแม้ว่า :-p
binki

5
โปรดทราบ : หากคุณใช้fs.createWriteStreamงานflagsอยู่ให้ใช้ หากคุณกำลังใช้fs.writeFileงานflagอยู่ โปรดอ้างอิงNode JS Docs - ระบบไฟล์สำหรับข้อมูลเพิ่มเติม
Anish Nair

2
ระวัง! พารามิเตอร์ไม่ใช่ "แฟล็ก" แต่ "แฟล็ก" (เอกพจน์): nodejs.org/api/…
Benny Neugebauer

25

นอกจากนี้appendFileคุณยังสามารถส่งค่าสถานะwriteFileเพื่อผนวกข้อมูลไปยังไฟล์ที่มีอยู่

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

เมื่อผ่านการตั้งค่าสถานะ 'a' ข้อมูลจะถูกต่อท้ายไฟล์


3
โปรดทราบ : หากคุณใช้fs.createWriteStreamงานflagsอยู่ให้ใช้ หากคุณกำลังใช้fs.writeFileงานflagอยู่ โปรดอ้างอิงNode JS Docs - ระบบไฟล์สำหรับข้อมูลเพิ่มเติม
Anish Nair

19

คุณต้องเปิดมันแล้วเขียนมัน

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

นี่คือลิงค์บางส่วนที่จะช่วยอธิบายพารามิเตอร์

เปิดการ
เขียน
อย่างใกล้ชิด


แก้ไข : คำตอบนี้ไม่ถูกต้องอีกต่อไปดูเป็นวิธีการfs.appendFileใหม่สำหรับการผนวก


1
ดูเหมือน supercobra เขียนบันทึกลงในไฟล์บันทึกอย่างต่อเนื่องไม่แนะนำให้ใช้ fs.write ในกรณีนี้ให้ใช้ fs.createWriteStream แทน อ่านnodejs.org/docs/v0.4.8/api/all.html#fs.write
kiwi โกรธ

10
คำตอบนั้นไม่แม่นยำอีกต่อไปตั้งแต่ nodejs v0.4.10
Ruben Tan

ควรเป็น '0666' มากกว่า 666
Ya Zhuang


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

5
การซิงค์ใด ๆ () มักเป็นความคิดที่ไม่ดีเว้นแต่คุณจะแน่ใจ 100% ว่าคุณต้องการ ถึงอย่างนั้นคุณอาจทำผิด
Zane Claes

4
ไม่ได้หมายความว่ามันผิด มันทำแบบซิงโครนัส อาจไม่ใช่วิธีปฏิบัติที่ดีที่สุดสำหรับ Node.js แต่ได้รับการสนับสนุน
Luis R.

2
ฉันใช้คำว่า "คุณทำผิด" ในความหมายของวลีภาษาอังกฤษทางอินเทอร์เน็ต เห็นได้ชัดว่ารองรับ = P
Zane Claes

7
เห็นด้วยกับ async แต่บางครั้งถ้าคุณแค่เขียนสคริปต์แบบโต้ตอบการซิงค์ก็ใช้ได้
bryanmac

7
การเขียนแบบซิงโครนัสนั้นก็โอเคอย่างแน่นอนหากคุณกำลังทำแอพบรรทัดคำสั่งผู้ใช้เดี่ยว (เช่นสคริปต์เพื่อทำบางสิ่ง) วิธีนี้จะเร็วกว่าในการทำสิ่งต่าง ๆ ทำไมโหนดถึงมีวิธีการซิงค์หากไม่ใช่เพื่อจุดประสงค์นี้
ม.ค. Święcki

3

หากคุณต้องการวิธีที่ง่ายและปราศจากความเครียดในการเขียนบันทึกแบบทีละบรรทัดในไฟล์ฉันแนะนำ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');

ทดสอบกับโหนด v8.9.4


2

วิธีการของฉันค่อนข้างพิเศษ ฉันเป็นพื้นใช้WriteStreamวิธีการแก้ปัญหา แต่ไม่จริง 'ปิด' FD stream.end()โดยใช้ แต่ผมใช้/cork uncorkนี่เป็นข้อดีของการใช้ RAM ต่ำ (ถ้ามีความสำคัญกับทุกคน) และฉันเชื่อว่ามันปลอดภัยกว่าที่จะใช้สำหรับการบันทึก / บันทึก (กรณีการใช้งานดั้งเดิมของฉัน)

ต่อไปนี้เป็นตัวอย่างที่ค่อนข้างง่าย แจ้งให้ทราบforล่วงหน้าฉันเพิ่งเพิ่มลูปหลอกสำหรับการแสดง - ในรหัสการผลิตฉันกำลังรอข้อความ websocket

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

uncork จะล้างข้อมูลไปยังไฟล์ในขีดถัดไป

ในสถานการณ์ของฉันมีจำนวนสูงสุดถึง ~ 200 เขียนต่อวินาทีในขนาดต่าง ๆ ในช่วงเวลากลางคืน แต่จำเป็นต้องเขียนเพียงไม่กี่ครั้งต่อนาที รหัสทำงานได้อย่างน่าเชื่อถือแม้ในช่วงเวลาเร่งด่วน


1

ฉันเสนอคำแนะนำนี้เพียงบางครั้งเนื่องจากการควบคุมการเปิดแฟล็กจะมีประโยชน์ตัวอย่างเช่นคุณอาจต้องการตัดทอนไฟล์ที่มีอยู่ก่อนจากนั้นจึงเพิ่มชุดการเขียนลงไป - ในกรณีนี้ให้ใช้แฟล็ก'w' เมื่อเปิดไฟล์ และอย่าปิดมันจนกว่าการเขียนทั้งหมดจะเสร็จสิ้น แน่นอนผนวกไฟล์อาจเป็นสิ่งที่คุณหลังจาก :-)

  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

การใช้fs.appendFileหรือfsPromises.appendFileเป็นตัวเลือกที่เร็วที่สุดและแข็งแกร่งที่สุดเมื่อคุณต้องการผนวกบางสิ่งเข้ากับไฟล์

ในทางตรงกันข้ามกับบางคำตอบที่แนะนำถ้าเส้นทางของไฟล์จะถูกส่งไปยังappendFileฟังก์ชั่น มันจริงปิดด้วยตัวเอง เฉพาะเมื่อคุณส่งผ่าน filehandle ที่คุณได้รับจากสิ่งที่fs.open()คุณต้องระวังในการปิด

ฉันลองมันด้วยไฟล์กว่า 50,000 บรรทัด

ตัวอย่าง :

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

ป้อนคำอธิบายรูปภาพที่นี่

Ref: https://github.com/nodejs/node/issues/7560
ตัวอย่างการดำเนินการ: https://github.com/apickjs/apickFS/blob/master/writeLines.js


0

นี่คือสคริปต์แบบเต็ม กรอกชื่อไฟล์ของคุณและเรียกใช้และมันจะทำงานได้! นี่คือวิดีโอสอนเกี่ยวกับตรรกะเบื้องหลังสคริปต์

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

วิธีที่ง่ายกว่าในการทำเช่นนี้คือ

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

ง่ายกว่าอะไร คุณได้ตรวจสอบคำตอบด้านบนซึ่งให้appendFileSyncโซลูชั่นเดียวกัน
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

นอกเหนือจากคำตอบของ denysoniqueบางครั้งก็ใช้ประเภท asynchronous appendFileและ async อื่น ๆ ใน NodeJS ที่สัญญาจะส่งคืนแทนที่จะส่งกลับผ่าน ในการทำเช่นนั้นคุณต้องห่อฟังก์ชันด้วยpromisifyHOF หรือนำเข้าฟังก์ชัน async จากเนมสเปซที่ทำสัญญา:

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

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

ฉันหวังว่ามันจะช่วยได้😉

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.