สร้างไดเรกทอรีเมื่อเขียนไฟล์ใน Node.js


134

ฉันกำลังซ่อมแซม Node.js และพบปัญหาเล็กน้อย dataฉันมีสคริปต์ซึ่งอยู่ในไดเรกทอรีที่เรียกว่า ฉันต้องการให้สคริปต์เขียนข้อมูลบางอย่างไปยังไฟล์ในไดเรกทอรีย่อยภายในdataไดเรกทอรีย่อย อย่างไรก็ตามฉันได้รับข้อผิดพลาดต่อไปนี้:

{ [Error: ENOENT, open 'D:\data\tmp\test.txt'] errno: 34, code: 'ENOENT', path: 'D:\\data\\tmp\\test.txt' }

รหัสดังต่อไปนี้:

var fs = require('fs');
fs.writeFile("tmp/test.txt", "Hey there!", function(err) {
    if(err) {
        console.log(err);
    } else {
        console.log("The file was saved!");
    }
}); 

ใครสามารถช่วยฉันในการหาวิธีที่จะทำให้ Node.js สร้างโครงสร้างไดเรกทอรีถ้ามันไม่ออกจากการเขียนไปยังไฟล์?


1
fs.promises.mkdir(path.dirname("tmp/test.txt"), {recursive: true}).then(x => fs.promises.writeFile("tmp/test.txt", "Hey there!"))
Offenso

คำตอบ:


127

โหนด> 10.12.0

fs.mkdirตอนนี้ยอมรับ{ recursive: true }ตัวเลือกดังนี้:

// Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
});

หรือด้วยสัญญา:

fs.promises.mkdir('/tmp/a/apple', { recursive: true }).catch(console.error);

โหนด <= 10.11.0

คุณสามารถแก้ปัญหานี้กับแพคเกจเหมือนmkdirpหรือFS-พิเศษ หากคุณไม่ต้องการติดตั้งแพคเกจโปรดดูคำตอบของ Tiago Peres Françaด้านล่าง


4
นั่นคือสิ่งที่ฉันจะไปด้วย ... สถิติเหล่านั้นชนะฉัน
Aran Mulholland

โปรดทราบว่าfs.promisesยังคงมีการทดลองnodejs.org/dist/latest-v10.x/docs/api/…
lasec0203

132

หากคุณไม่ต้องการใช้แพ็คเกจเพิ่มเติมคุณสามารถเรียกใช้ฟังก์ชันต่อไปนี้ก่อนสร้างไฟล์ของคุณ:

var path = require('path'),
    fs = require('fs');

function ensureDirectoryExistence(filePath) {
  var dirname = path.dirname(filePath);
  if (fs.existsSync(dirname)) {
    return true;
  }
  ensureDirectoryExistence(dirname);
  fs.mkdirSync(dirname);
}

2
สิ่งนี้ควรใช้statSyncแทนexistsSyncโดยอิงจากstackoverflow.com/questions/4482686/…
GavinR

1
pathนอกจากนี้ยังมีแพคเกจที่ต้องจำเป็นเช่นเดียวกับfs: var path = require('path')ในกรณีที่คนสงสัย ดูเอกสารคู่มือโหนด
Rafael Emshoff

9
fs.existsSyncจะไม่เลิกเพียงfs.existsคือ
zzzzBov

6
มีความสับสนเล็กน้อยเกี่ยวกับว่าฟังก์ชัน fs.existsSync เลิกใช้แล้วหรือไม่ ตอนแรกจากความเข้าใจของฉันฉันคิดว่ามันเป็นดังนั้นฉันจึงปรับปรุงคำตอบเพื่อสะท้อนถึงสิ่งนี้ แต่ตอนนี้ตามที่ระบุโดย @zzzzBov เอกสารระบุอย่างชัดเจนว่ามีเพียง fs.exists เท่านั้นที่เลิกใช้งานการใช้ fs.existsSync ยังคงใช้ได้ ด้วยเหตุนี้ฉันจึงลบรหัสก่อนหน้าและคำตอบของฉันตอนนี้มีเพียงโซลูชันที่ง่ายกว่า (ด้วยการใช้ fs.existsSync)
Tiago Peres França

1
@chrismarx จินตนาการถึงเส้นทางต่อไปนี้: "/home/documents/a/b/c/myfile.txt" มี "/ home / เอกสาร" อยู่ในขณะที่ทุกอย่างที่อยู่ข้างหน้าจะไม่มี เมื่อเรียกว่า "sureDirectoryExistence" เป็นครั้งแรก dirname คือ "/ home / เอกสาร / a / b / c" ฉันไม่สามารถโทร fs.mkdirSync (dirname) ได้ในขณะนี้เนื่องจากไม่มี "/ home / เอกสาร / a / b" ในการสร้างไดเรกทอรี "c" ฉันต้องตรวจสอบให้แน่ใจก่อนว่ามี "/ home / เอกสาร / a / b"
Tiago Peres França

43

ด้วยnode-fs-extraคุณสามารถทำได้อย่างง่ายดาย

ติดตั้ง

npm install --save fs-extra

จากนั้นใช้outputFileวิธีการ เอกสารอธิบายว่า:

เกือบจะเหมือนกับ writeFile (เช่นมันเขียนทับ) ยกเว้นว่าถ้าไม่มีไดเรกทอรีหลักมันจะถูกสร้างขึ้น

คุณสามารถใช้มันได้สามวิธี:

สไตล์การโทรกลับ

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

fse.outputFile('tmp/test.txt', 'Hey there!', err => {
  if(err) {
    console.log(err);
  } else {
    console.log('The file was saved!');
  }
})

ใช้สัญญา

หากคุณใช้สัญญาและฉันหวังว่านี่คือรหัส:

fse.outputFile('tmp/test.txt', 'Hey there!')
   .then(() => {
       console.log('The file was saved!');
   })
   .catch(err => {
       console.error(err)
   });

รุ่นซิงค์

หากคุณต้องการเวอร์ชั่นซิงค์เพียงใช้รหัสนี้:

fse.outputFileSync('tmp/test.txt', 'Hey there!')

สำหรับการอ้างอิงที่สมบูรณ์ให้ตรวจสอบoutputFileเอกสารและวิธีการที่สนับสนุนโหนด -s- พิเศษทั้งหมด


26

แจ้งเตือนปลั๊กไร้ยางอาย!

คุณจะต้องตรวจสอบแต่ละไดเรกทอรีในโครงสร้างเส้นทางที่คุณต้องการและสร้างมันเองถ้ามันไม่มีอยู่ เครื่องมือทั้งหมดที่ทำเช่นนั้นมีอยู่แล้วในโมดูล fs ของโหนด แต่คุณสามารถทำสิ่งนั้นได้อย่างง่ายดายด้วยโมดูล mkpath ของฉัน: https://github.com/jrajav/mkpath


1
ว่าจะสร้างไฟล์โดยตรงหรือเพียงแค่โครงสร้างไดเรกทอรี? ฉันกำลังมองหาโซลูชันที่สร้างไฟล์พร้อมกับโครงสร้างไดเรกทอรีเมื่อสร้างไฟล์
Hirvesh

เพียงแค่โครงสร้างไดเรกทอรี คุณต้องการ mkdir / path ก่อนและหากไม่มีข้อผิดพลาดให้เขียนไฟล์ของคุณต่อไป มันจะง่ายพอที่จะเขียนฟังก์ชั่นในการทำทั้งสองอย่างพร้อมกันให้เต็มพา ธ ไปยังไฟล์ที่จะเขียน - เพียงแค่แยกชื่อไฟล์โดยใช้path.basename
jrajav

1
ในความเป็นจริงก็คือเพื่อให้ง่ายที่ผมเขียนไว้ใน 2 นาที :) (ยังไม่ทดลอง)
jrajav

อัปเดต: ทดสอบและแก้ไขแล้วโปรดลองอีกครั้งหากไม่ได้ผลในครั้งแรก
jrajav

8
@Kiyura มันแตกต่างจากmkdirp ที่ใช้กันอย่างแพร่หลายอย่างไร?
David Weldon

9

เนื่องจากฉันยังไม่สามารถแสดงความคิดเห็นได้ฉันกำลังโพสต์คำตอบที่ได้รับการปรับปรุงตาม @ tiago-peres-françaทางออกที่ยอดเยี่ยม (ขอบคุณ!) รหัสของเขาไม่ได้สร้างไดเรกทอรีในกรณีที่มีเพียงไดเรกทอรีสุดท้ายที่หายไปในเส้นทางเช่นอินพุตคือ "C: / test / abc" และ "C: / test" มีอยู่แล้ว นี่เป็นตัวอย่างข้อมูลที่ใช้งานได้:

function mkdirp(filepath) {
    var dirname = path.dirname(filepath);

    if (!fs.existsSync(dirname)) {
        mkdirp(dirname);
    }

    fs.mkdirSync(filepath);
}

1
นั่นเป็นเพราะโซลูชันของ @ tiago ต้องการเส้นทางไฟล์ ในกรณีของคุณabcถูกตีความว่าเป็นไฟล์ที่คุณต้องการสร้างไดเรกทอรี ยังสร้างไดเรกทอรีเพิ่มไฟล์หุ่นเส้นทางของคุณเช่นabc C:/test/abc/dummy.txt
Sphinxxx

ใช้แบบเรียกซ้ำ:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

1
@Offenso มันเป็นทางออกที่ดีที่สุด แต่สำหรับ Node.js เวอร์ชัน 10.12 ขึ้นไปเท่านั้น
Nickensoul

8

คำแนะนำของฉันคือพยายามอย่าพึ่งพาการพึ่งพาเมื่อคุณสามารถทำได้โดยง่ายด้วยโค้ดสองสามบรรทัด

นี่คือสิ่งที่คุณพยายามที่จะบรรลุใน14บรรทัดของรหัส:

fs.isDir = function(dpath) {
    try {
        return fs.lstatSync(dpath).isDirectory();
    } catch(e) {
        return false;
    }
};
fs.mkdirp = function(dirname) {
    dirname = path.normalize(dirname).split(path.sep);
    dirname.forEach((sdir,index)=>{
        var pathInQuestion = dirname.slice(0,index+1).join(path.sep);
        if((!fs.isDir(pathInQuestion)) && pathInQuestion) fs.mkdirSync(pathInQuestion);
    });
};

1
บรรทัดที่สามจะไม่ดีกว่านี้ไหม return fs.lstatSync(dpath).isDirectory()มิฉะนั้นจะเกิดอะไรขึ้นถ้า isDirectory () ส่งคืนค่าเท็จ
Giorgio Aresu

2
ใช้แบบเรียกซ้ำ:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

1
@Offenso ไม่รองรับโหนด 8
Ievgen Naida

2

ฉันเพิ่งเผยแพร่โมดูลนี้เพราะฉันต้องการฟังก์ชั่นนี้

https://www.npmjs.org/package/filendir

มันทำงานเหมือน wrapper รอบวิธี Node.js fs ดังนั้นคุณสามารถใช้มันได้ในลักษณะเดียวกับที่คุณใช้fs.writeFileและfs.writeFileSync(ทั้ง async และ synchronous write)

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