Tạo thư mục khi ghi vào tệp trong Node.js


134

Tôi đã mày mò với Node.js và thấy một vấn đề nhỏ. Tôi đã có một tập lệnh nằm trong một thư mục được gọi data. Tôi muốn tập lệnh ghi một số dữ liệu vào một tệp trong thư mục con trong datathư mục con. Tuy nhiên tôi nhận được lỗi sau:

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

Mã như sau:

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

Ai đó có thể giúp tôi tìm ra cách làm cho Node.js tạo cấu trúc thư mục nếu nó không thoát ra để ghi vào tệp không?


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

Câu trả lời:


127

Nút> 10.12.0

fs.mkdir hiện chấp nhận một { recursive: true }tùy chọn như vậy:

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

hoặc với một lời hứa:

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

Nút <= 10.11.0

Bạn có thể giải quyết điều này bằng một gói như mkdirp hoặc fs-Extra . Nếu bạn không muốn cài đặt một gói, vui lòng xem câu trả lời của Tiago Peres França bên dưới.


4
Đó là người tôi đang đi với ... những chỉ số đó đã thắng tôi.
Aran Mulholland

lưu ý rằng đó fs.promisesvẫn là thử nghiệm nodejs.org/dist/latest-v10.x/docs/api/iêu
lasec0203

132

Nếu bạn không muốn sử dụng bất kỳ gói bổ sung nào, bạn có thể gọi hàm sau trước khi tạo tệp của mình:

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
Điều này nên sử dụng statSyncthay vì existsSync, dựa trên stackoverflow.com/questions/4482686/
Khăn

1
pathcũng là một gói cần phải được yêu cầu giống như fs: var path = require('path')trong trường hợp bất cứ ai đang tự hỏi. Xem tài liệu nút .
Rafael Emshoff


6
Có một số nhầm lẫn về việc chức năng fs.existsSync có bị phản đối hay không. Lúc đầu, theo sự hiểu biết của tôi, tôi nghĩ là vậy, vì vậy tôi đã cập nhật câu trả lời để phản ánh điều này. Nhưng bây giờ, như được chỉ ra bởi @zzzzBov, tài liệu nêu rõ rằng chỉ fs.exists đã không được chấp nhận, việc sử dụng fs.existsSync vẫn còn hiệu lực. Vì lý do này, tôi đã xóa mã trước đó và câu trả lời của tôi bây giờ chỉ chứa giải pháp đơn giản hơn (với việc sử dụng fs.existsSync).
Tiago Peres França

1
@chrismarx hãy tưởng tượng đường dẫn sau: "/home/document/a/b/c/myfile.txt". "/ Nhà / tài liệu" tồn tại, trong khi mọi thứ ở phía trước thì không. Khi "notifyDirectoryExistence" được gọi lần đầu tiên, dirname là "/ home / Documents / a / b / c". Tôi không thể gọi fs.mkdirSync (dirname) ngay bây giờ vì "/ home / Documents / a / b" cũng không tồn tại. Để tạo thư mục "c", trước tiên tôi cần đảm bảo sự tồn tại của "/ home / Documents / a / b".
Tiago Peres França

43

Với nút-fs-thêm bạn có thể làm điều đó một cách dễ dàng.

Cài đặt nó

npm install --save fs-extra

Sau đó sử dụng outputFilephương pháp. Tài liệu của nó nói:

Gần giống như writeFile (nghĩa là nó ghi đè lên), ngoại trừ việc nếu thư mục mẹ không tồn tại, nó được tạo.

Bạn có thể sử dụng nó theo ba cách:

Kiểu gọi lại

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

Sử dụng lời hứa

Nếu bạn sử dụng lời hứa và tôi hy vọng vậy, đây là mã:

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

Phiên bản đồng bộ hóa

Nếu bạn muốn có một phiên bản đồng bộ, chỉ cần sử dụng mã này:

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

Để tham khảo đầy đủ, hãy kiểm tra outputFiletài liệu và tất cả các phương thức được hỗ trợ thêm của nút-fs .


26

Không biết xấu hổ cắm cảnh báo!

Bạn sẽ phải kiểm tra từng thư mục trong cấu trúc đường dẫn bạn muốn và tạo nó theo cách thủ công nếu nó không tồn tại. Tất cả các công cụ để làm như vậy đã có sẵn trong mô-đun fs của Node, nhưng bạn có thể thực hiện tất cả những điều đó chỉ với mô-đun mkpath của tôi: https://github.com/jrajav/mkpath


1
Điều đó sẽ tạo ra các tập tin trực tiếp hoặc chỉ cấu trúc thư mục? Tôi đang tìm một giải pháp tạo tệp cùng với cấu trúc thư mục khi tạo tệp.
Hirvesh

Chỉ là cấu trúc thư mục. Trước tiên, bạn sẽ mkdir / path và nếu không có lỗi, hãy tiến hành viết tệp của bạn. Sẽ đủ đơn giản để viết một hàm để thực hiện đồng thời cả hai, đưa ra một đường dẫn đầy đủ đến một tệp để viết - chỉ cần tách tên tệp bằng path.basename
jrajav

1
Trên thực tế, nó đơn giản đến mức tôi đã viết nó trong 2 phút . :) (Chưa được kiểm tra)
jrajav

Cập nhật: Đã kiểm tra và chỉnh sửa, vui lòng thử lại nếu nó không hoạt động lần đầu tiên.
jrajav

8
@Kiyura Điều này khác với mkdirp được sử dụng rộng rãi như thế nào?
David Weldon

9

Vì tôi chưa thể bình luận, tôi đang đăng một câu trả lời nâng cao dựa trên giải pháp tuyệt vời @ tiago-peres-frança (cảm ơn!). Mã của anh ta không tạo thư mục trong trường hợp chỉ thiếu thư mục cuối cùng trong đường dẫn, ví dụ: đầu vào là "C: / test / abc" và "C: / test" đã tồn tại. Đây là một đoạn hoạt động:

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

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

    fs.mkdirSync(filepath);
}

1
Đó là bởi vì giải pháp của @ tiago mong đợi một đường dẫn tệp . Trong trường hợp của bạn, abcđược hiểu là tập tin bạn cần để tạo một thư mục cho. Để tạo abcthư mục, hãy thêm một tập tin giả vào đường dẫn của bạn, vd C:/test/abc/dummy.txt.
Nhân sư

Sử dụng đệ quy:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

1
@ Offerenso đó là giải pháp tốt nhất, nhưng chỉ dành cho Node.js phiên bản 10.12 trở lên.
Nickensoul

8

Lời khuyên của tôi là: cố gắng không phụ thuộc vào phụ thuộc khi bạn có thể dễ dàng làm điều đó với một vài dòng mã

Đây là những gì bạn đang cố gắng đạt được trong 14 dòng mã:

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
Không phải dòng thứ ba sẽ tốt hơn như thế này sao? return fs.lstatSync(dpath).isDirectory(), nếu không thì điều gì sẽ xảy ra nếu isDirectory () trả về false?
Giorgio Aresu

2
Sử dụng đệ quy:fs.promises.mkdir(path.dirname(file), {recursive: true}).then(x => fs.promises.writeFile(file, data))
Offenso

1
@ Offerenso nó không được hỗ trợ bởi nút 8
Ievgen Naida

2

Tôi vừa xuất bản mô-đun này vì tôi cần chức năng này.

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

Nó hoạt động như một trình bao bọc xung quanh các phương thức fs của Node.js. Vì vậy, bạn có thể sử dụng chính xác theo cách bạn muốn fs.writeFilefs.writeFileSync(cả ghi không đồng bộ và ghi đồng bộ)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.