Làm cách nào để tạo một thư mục nếu nó không tồn tại bằng Node.js?


Câu trả lời:


1279
var fs = require('fs');
var dir = './tmp';

if (!fs.existsSync(dir)){
    fs.mkdirSync(dir);
}

28
Nếu bạn đang thực hiện thao tác này khi khởi động hoặc khởi tạo ứng dụng, thì việc chặn thực thi là tốt nếu bạn thực hiện điều tương tự nếu bạn thực hiện không đồng bộ. Nếu bạn đang tạo một thư mục như là một hoạt động định kỳ thì thực tế xấu của nó nhưng có lẽ sẽ không gây ra bất kỳ vấn đề nào về hiệu năng, nhưng nó không phải là vấn đề tồi. Chỉ sử dụng để khởi động ứng dụng của bạn hoặc hoạt động một lần.
tsturzl

20
existsSync () không tán thành, tồn tại () là mặc dù - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick

sử dụng Synccác phương thức * thường là không có: không muốn chặn vòng lặp sự kiện
Max Heiber

14
Sử dụng các phương thức đồng bộ hóa là tốt cho các tập lệnh cục bộ và như vậy, rõ ràng không phải là một ý tưởng tốt cho máy chủ.
Cầu tàu

Nếu khối đó được bao quanh bởi setTimeout, thì nó không đồng bộ .....................
Bryan Grace

185

Không, vì nhiều lý do.

  1. Các pathmô-đun không có một exists/ existsSyncphương pháp. Nó nằm trong fsmô-đun. (Có lẽ bạn vừa mắc lỗi đánh máy trong câu hỏi của bạn?)

  2. Các tài liệu rõ ràng không khuyến khích bạn sử dụng exists.

    fs.exists()là lỗi thời và chỉ tồn tại vì lý do lịch sử. Hầu như không bao giờ có một lý do để sử dụng nó trong mã của riêng bạn.

    Cụ thể, kiểm tra xem một tệp có tồn tại trước khi mở tệp đó là một mẫu chống khiến bạn dễ bị tổn thương trong điều kiện chủng tộc hay không: một quy trình khác có thể xóa tệp giữa các lệnh gọi đến fs.exists()fs.open(). Chỉ cần mở tệp và xử lý lỗi khi nó không ở đó.

    Vì chúng ta đang nói về một thư mục chứ không phải là một tập tin, lời khuyên này ngụ ý bạn chỉ nên gọi mkdirvà bỏ qua vô điều kiện EEXIST.

  3. Nói chung, bạn nên tránh *Sync phương thức . Họ đang chặn, điều đó có nghĩa là hoàn toàn không có gì khác trong chương trình của bạn có thể xảy ra trong khi bạn vào đĩa. Đây là một hoạt động rất tốn kém và thời gian cần thiết để phá vỡ giả định cốt lõi của vòng lặp sự kiện của nút.

    Các Syncphương thức * thường ổn trong các tập lệnh nhanh đơn mục đích (các tập lệnh thực hiện một việc rồi thoát), nhưng hầu như không bao giờ được sử dụng khi bạn viết máy chủ: máy chủ của bạn sẽ không thể phản hồi cho bất kỳ ai trong toàn bộ thời gian của các yêu cầu I / O. Nếu nhiều yêu cầu của máy khách yêu cầu thao tác I / O, máy chủ của bạn sẽ nhanh chóng bị dừng lại.


    Lần duy nhất tôi xem xét sử dụng Synccác phương thức * trong ứng dụng máy chủ là trong một hoạt động xảy ra một lần (và chỉ một lần), khi khởi động. Ví dụ, require thực sự sử dụngreadFileSync để tải các mô-đun.

    Ngay cả khi đó, bạn vẫn phải cẩn thận vì nhiều I / O đồng bộ có thể làm chậm thời gian khởi động máy chủ của bạn một cách không cần thiết.


    Thay vào đó, bạn nên sử dụng các phương thức I / O không đồng bộ.

Vì vậy, nếu chúng ta kết hợp những lời khuyên đó, chúng ta sẽ nhận được một cái gì đó như thế này:

function ensureExists(path, mask, cb) {
    if (typeof mask == 'function') { // allow the `mask` parameter to be optional
        cb = mask;
        mask = 0777;
    }
    fs.mkdir(path, mask, function(err) {
        if (err) {
            if (err.code == 'EEXIST') cb(null); // ignore the error if the folder already exists
            else cb(err); // something else went wrong
        } else cb(null); // successfully created folder
    });
}

Và chúng ta có thể sử dụng nó như thế này:

ensureExists(__dirname + '/upload', 0744, function(err) {
    if (err) // handle folder creation error
    else // we're all good
});

Tất nhiên, điều này không giải thích cho các trường hợp cạnh như

  • Điều gì xảy ra nếu thư mục bị xóa trong khi chương trình của bạn đang chạy? (giả sử bạn chỉ kiểm tra xem nó có tồn tại một lần trong khi khởi động không)
  • Điều gì xảy ra nếu thư mục đã tồn tại nhưng với quyền sai?

1
Có cách nào để tránh SyntaxError: Octal không được phép ở chế độ nghiêm ngặt?
Người đánh cá

8
Viết nó dưới dạng thập phân. 0744 == 484.
josh3736

3
Một cách khác là sử dụng một mô-đun mở rộng fs để có chức năng này, chẳng hạn như github.com/jprichardson/node-fs-extra
Bret

lá cờ "mặt nạ" này có còn phù hợp trong năm 2019 không? mục đích của nó là gì?
oldboy

Đó là chế độ tập tin unix - quyền đọc / ghi của thư mục.
josh3736


33

Các mkdirphương pháp có khả năng đệ quy tạo bất kỳ thư mục trong một con đường mà không làm tồn tại, và bỏ qua những cái mà làm.

Từ các tài liệu Node v10 / 11 :

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

LƯU Ý: Bạn sẽ cần nhập phần tích hợp fs mô-đun tích hợp trước.

Bây giờ đây là một ví dụ mạnh mẽ hơn một chút, tận dụng các Mô-đun ES gốc (có bật cờ và mở rộng .mjs), xử lý các đường dẫn không gốc và chiếm các tên đường dẫn đầy đủ:

import fs from 'fs';
import path from 'path';

createDirectories(pathname) {
   const __dirname = path.resolve();
   pathname = pathname.replace(/^\.*\/|\/?[^\/]+\.[a-z]+|\/$/g, ''); // Remove leading directory markers, and remove ending /file-name.extension
   fs.mkdir(path.resolve(__dirname, pathname), { recursive: true }, e => {
       if (e) {
           console.error(e);
       } else {
           console.log('Success');
       }
    });
}

Bạn có thể sử dụng nó như thế nào createDirectories('/components/widget/widget.js');.

Và tất nhiên, có lẽ bạn sẽ muốn trở nên lạ mắt hơn bằng cách sử dụng các lời hứa với async / await để thúc đẩy việc tạo tệp theo cách nhìn đồng bộ dễ đọc hơn khi các thư mục được tạo; nhưng, điều đó vượt quá phạm vi câu hỏi.


1
Tại sao const __dirname = path.resolve (); và không sử dụng __dirname tích hợp?
TamusJRoyce

29

Chỉ trong trường hợp bất kỳ ai quan tâm đến phiên bản một dòng. :)

//or in typescript: import * as fs from 'fs';
const fs = require('fs');
!fs.existsSync(dir) && fs.mkdirSync(dir);

Bị cáo buộc 1-lót không thực sự 1 dòng.
Lai web dev

20

Bạn chỉ có thể sử dụng mkdirvà bắt lỗi nếu thư mục tồn tại.
Đây là async (vì vậy thực hành tốt nhất) và an toàn.

fs.mkdir('/path', err => { 
    if (err && err.code != 'EEXIST') throw 'up'
    .. safely do your stuff here  
    })

(Tùy chọn thêm đối số thứ hai với chế độ.)


Những suy nghĩ khác:

  1. Bạn có thể sử dụng sau đó hoặc chờ đợi bằng cách sử dụng hứa hẹn bản địa .

    const util = require('util'), fs = require('fs');
    const mkdir = util.promisify(fs.mkdir);
    var myFunc = () => { ..do something.. } 
    
    mkdir('/path')
        .then(myFunc)
        .catch(err => { if (err.code != 'EEXIST') throw err; myFunc() })
  2. Bạn có thể thực hiện phương thức hứa của riêng mình, đại loại như (chưa được kiểm tra):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. Để kiểm tra đồng bộ, bạn có thể sử dụng:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. Hoặc bạn có thể sử dụng một thư viện, hai thứ phổ biến nhất

    • mkdirp (chỉ cần thư mục)
    • fsextra (supersets fs, thêm nhiều thứ hữu ích)

1
đối với phương pháp hứa hẹn số 1, bạn có thể sắp xếp lại việc đánh bắt. mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
Điều gì sẽ tuyệt vời vào

Và sử dụng !==thay vì!=
Quentin Roy

18

Với gói fs-Extra bạn có thể thực hiện việc này với một lớp lót :

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

const dir = '/tmp/this/path/does/not/exist';
fs.ensureDirSync(dir);

Thật là một câu trả lời bị đánh giá thấp! fs-Extra có bacame phải có cho tôi. Tôi nghĩ rằng việc viết hơn 10 dòng chỉ để kiểm tra xem thư mục có tồn tại không ...
538ROMEO

10

Giải pháp tốt nhất sẽ là sử dụng mô-đun npm có tên là nút-fs-thêm . Nó có một phương thức gọi là mkdirtạo thư mục mà bạn đề cập. Nếu bạn đưa ra một đường dẫn thư mục dài, nó sẽ tự động tạo các thư mục mẹ. Mô-đun này là một bộ siêu mô-đun npm fs, vì vậy bạn cũng có thể sử dụng tất cả các chức năng fsnếu bạn thêm mô-đun này.


6
var dir = 'path/to/dir';
try {
  fs.mkdirSync(dir);
} catch(e) {
  if (e.code != 'EEXIST') throw e;
}

4
Đối với Node.js v7.4.0, các trạng thái tài liệu không fs.exists()được dùng nữa, nhưng fs.existsSync()không phải. Bạn có thể thêm một liên kết đến một nguồn tài nguyên nói rằng đã fs.existsSync()được khấu hao?
francis

1
Câu trả lời chỉ có mã không hữu ích cho người dùng đến câu hỏi này trong tương lai. Vui lòng chỉnh sửa câu trả lời của bạn để giải thích lý do tại sao mã của bạn giải quyết được vấn đề ban đầu
yivi


1
Cảm ơn! Có vẻ như chức năng tồn tại trong phiên bản 0.12, đã bị từ chối trong phiên bản 4 và 5 và được khôi phục trong phiên bản 6 và 7 ... Loại chức năng zombi ...
francis

1
Có, hiện tại nó KHÔNG bị phản đối kể từ Apr 2018: nodejs.org/api/fs.html#fs_fs_existssync_path
LeOn - Han Li

5
    var filessystem = require('fs');
    var dir = './path/subpath/';

    if (!filessystem.existsSync(dir)){
        filessystem.mkdirSync(dir);
    }else
    {
        console.log("Directory already exist");
    }

Điều này có thể giúp bạn :)


5

ENOENT: không có tập tin hoặc thư mục như vậy

Giải pháp

const fs = require('fs')  // in javascript
import * as fs from "fs"  // in typescript
import fs from "fs"       // in typescript

// it will create the directory if it does not exist.
!fs.existsSync(`./assets/`) && fs.mkdirSync(`./assets/`, { recursive: true })

1
việc này hiệu quả, cảm ơn bạn
Aljohn Yamaro

3

Tôi muốn thêm một công cụ tái cấu trúc Promise của bản mô tả câu trả lời của josh3736 .

Nó thực hiện điều tương tự và có các trường hợp cạnh giống nhau, nó chỉ tình cờ sử dụng Promise, typedefs kiểu chữ và hoạt động với "sử dụng nghiêm ngặt".

// https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation
const allRWEPermissions = parseInt("0777", 8);

function ensureFilePathExists(path: string, mask: number = allRWEPermissions): Promise<void> {
    return new Promise<void>(
        function(resolve: (value?: void | PromiseLike<void>) => void,
            reject: (reason?: any) => void): void{
            mkdir(path, mask, function(err: NodeJS.ErrnoException): void {
                if (err) {
                    if (err.code === "EEXIST") {
                        resolve(null); // ignore the error if the folder already exists
                    } else {
                        reject(err); // something else went wrong
                    }
                } else {
                    resolve(null); // successfully created folder
                }
            });
    });
}

3

Với nút 10 + ES6:

import path from 'path';
import fs from 'fs';

(async () => {
  const dir = path.join(__dirname, 'upload');

  try {
    await fs.promises.mkdir(dir);
  } catch (error) {
    if (error.code === 'EEXIST') {
      // Something already exists, but is it a file or directory?
      const lstat = await fs.promises.lstat(dir);

      if (!lstat.isDirectory()) {
        throw error;
      }
    } else {
      throw error;
    }
  }
})();

2

Bạn có thể sử dụng nút File System lệnh fs.stat để kiểm tra xem thư mục tồn tại và fs.mkdir để tạo ra một thư mục với gọi lại, hoặc fs.mkdirSync để tạo ra một thư mục mà không gọi lại, như ví dụ sau:

//first require fs
const fs = require('fs');

// Create directory if not exist (function)
const createDir = (path) => {
    // check if dir exist
    fs.stat(path, (err, stats) => {
        if (stats.isDirectory()) {
            // do nothing
        } else {
            // if the given path is not a directory, create a directory
            fs.mkdirSync(path);
        }
    });
};

1

Đây là một chức năng nhỏ để tạo đệ quy các thư mục:

const createDir = (dir) => {
  // This will create a dir given a path such as './folder/subfolder' 
  const splitPath = dir.split('/');
  splitPath.reduce((path, subPath) => {
    let currentPath;
    if(subPath != '.'){
      currentPath = path + '/' + subPath;
      if (!fs.existsSync(currentPath)){
        fs.mkdirSync(currentPath);
      }
    }
    else{
      currentPath = subPath;
    }
    return currentPath
  }, '')
}

0

Sử dụng async / await:

const mkdirP = async (directory) => {
  try {
    return await fs.mkdirAsync(directory);
  } catch (error) {
    if (error.code != 'EEXIST') {
      throw e;
    }
  }
};

Bạn sẽ cần phải hứa fs:

import nodeFs from 'fs';
import bluebird from 'bluebird';

const fs = bluebird.promisifyAll(nodeFs);
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.