จะสร้างไดเรกทอรีได้อย่างไรหากไม่มีอยู่ใน Node.js


656

นี่เป็นวิธีที่ถูกต้องในการสร้างไดเรกทอรีหรือไม่หากไม่มีอยู่ ควรได้รับอนุญาตอย่างเต็มที่สำหรับสคริปต์และผู้อื่นสามารถอ่านได้

var dir = __dirname + '/upload';
if (!path.existsSync(dir)) {
    fs.mkdirSync(dir, 0744);
}

1
การทำซ้ำของNode.js ที่เป็น
Benny Neugebauer

คำตอบ:


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

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

28
หากคุณกำลังดำเนินการนี้กับการบู๊ตของแอพหรือการกำหนดค่าเริ่มต้นแล้วก็เป็นการดีที่จะบล็อกการดำเนินการเช่นเดียวกับที่คุณทำในสิ่งเดียวกันถ้าคุณทำมันเป็นแบบซิงค์ หากคุณกำลังสร้างไดเรกทอรีเป็นการดำเนินการที่เกิดขึ้นซ้ำแล้วล่ะก็มันก็มีข้อปฏิบัติที่ไม่ดี แต่อาจจะไม่ทำให้เกิดปัญหาเกี่ยวกับประสิทธิภาพใด ๆ แต่มันเป็นปัญหาที่ไม่ดีเลย ใช้สำหรับการบูตแอปของคุณหรือดำเนินการครั้งเดียวเท่านั้น
tsturzl

20
existSync () ไม่ได้ถูกคัดค้าน แต่มีอยู่ () คือ - nodejs.org/api/fs.html#fs_fs_existssync_path
Ian Chadwick

การใช้Syncเมธอด* มักจะไม่มีข้อห้าม: ไม่ต้องการบล็อกลูปของเหตุการณ์
Max Heiber

14
การใช้วิธีการซิงค์เป็นสิ่งที่ดีสำหรับสคริปต์ในเครื่องและไม่ใช่ความคิดที่ดีสำหรับเซิร์ฟเวอร์
ท่าเรือ

หากบล็อกนั้นถูกล้อมรอบด้วย setTimeout มันจะเป็นแบบซิงค์ .....................
ไบรอันเกรซ

185

ไม่ได้ด้วยเหตุผลหลายประการ

  1. pathโมดูลไม่ได้มีexists/ existsSyncวิธีการ มันอยู่ในfsโมดูล (บางทีคุณอาจพิมพ์ผิดในคำถามของคุณ?)

  2. เอกสารอย่างชัดเจนกีดกันexistsคุณจากการใช้

    fs.exists()เป็นสมัยและมีเหตุผลทางประวัติศาสตร์เท่านั้น ไม่ควรมีเหตุผลที่จะใช้มันในรหัสของคุณเอง

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

    เนื่องจากเรากำลังพูดถึงไดเรกทอรีมากกว่าไฟล์คำแนะนำนี้หมายถึงคุณควรเพียงแค่ไม่มีเงื่อนไขโทรและไม่สนใจmkdirEEXIST

  3. โดยทั่วไปคุณควรหลีกเลี่ยงSyncวิธี* พวกเขากำลังบล็อกซึ่งหมายความว่าไม่มีอะไรอย่างอื่นในโปรแกรมของคุณที่สามารถเกิดขึ้นได้ในขณะที่คุณไปที่ดิสก์ นี่คือการดำเนินการที่มีราคาแพงมากและเวลาที่ใช้ในการแบ่งสมมติฐานหลักของการวนรอบเหตุการณ์ของโหนด

    Syncวิธีการ* มักใช้กับสคริปต์ด่วนแบบใช้ครั้งเดียว (ซึ่งทำสิ่งใดสิ่งหนึ่งและจากนั้นออก) แต่แทบจะไม่ควรใช้เมื่อคุณเขียนเซิร์ฟเวอร์: เซิร์ฟเวอร์ของคุณจะไม่สามารถตอบสนองกับใครได้ตลอดระยะเวลาที่ผ่านมา ของคำขอ I / O หากคำขอของไคลเอนต์หลายรายการต้องการการดำเนินการ I / O เซิร์ฟเวอร์ของคุณจะหยุดทำงานอย่างรวดเร็ว


    ครั้งเดียวที่ฉันจะพิจารณาใช้Syncเมธอด* ในแอปพลิเคชันเซิร์ฟเวอร์คือการทำงานที่เกิดขึ้นครั้งเดียว (และเพียงครั้งเดียว) เมื่อเริ่มต้น ตัวอย่างเช่นrequire ใช้readFileSyncเพื่อโหลดโมดูลจริง ๆ

    ถึงอย่างนั้นคุณก็ยังต้องระวังเพราะ I / O แบบซิงโครนัสจำนวนมากสามารถชะลอเวลาเริ่มต้นเซิร์ฟเวอร์ของคุณโดยไม่จำเป็น


    คุณควรใช้เมธอด I / O แบบอะซิงโครนัสแทน

ดังนั้นหากเรารวมคำแนะนำเหล่านั้นเข้าด้วยกันเราจะได้อะไรเช่นนี้:

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

และเราสามารถใช้สิ่งนี้:

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

แน่นอนว่านี่ไม่ได้หมายถึงกรณีขอบเช่น

  • จะเกิดอะไรขึ้นหากโฟลเดอร์ถูกลบขณะที่โปรแกรมกำลังทำงานอยู่ (สมมติว่าคุณตรวจสอบว่ามีอยู่ครั้งเดียวระหว่างการเริ่มต้น)
  • จะเกิดอะไรขึ้นถ้าโฟลเดอร์มีอยู่แล้ว แต่ด้วยสิทธิ์ที่ผิด?

1
มีวิธีการหลีกเลี่ยง SyntaxError: ไม่อนุญาตให้ใช้ตัวอักษรฐานแปดในโหมดเข้มงวด
Whisher

8
เขียนเป็นทศนิยม 0744 == 484.
josh3736

3
อีกทางเลือกหนึ่งคือการใช้โมดูลที่ขยาย fs ให้มีฟังก์ชั่นนี้เช่นgithub.com/jprichardson/node-fs-extra
Bret

"หน้ากาก" นี้เป็นสิ่งธงยังคงเกี่ยวข้องในปี 2019? จุดประสงค์ของมันคืออะไร?
oldboy

มันเป็นโหมดไฟล์ unix - สิทธิ์ในการอ่าน / เขียนของไดเรกทอรี
josh3736


33

mkdirวิธีการมีความสามารถในการซ้ำสร้างไดเรกทอรีใด ๆ ในเส้นทางที่ไม่อยู่และไม่สนใจคนที่ทำ

จากNode v10 / 11 docs :

// 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โมดูลในตัวก่อน

ต่อไปนี้เป็นตัวอย่างที่มีประสิทธิภาพมากกว่านี้เล็กน้อยที่ใช้ประโยชน์จากโมดูล ES ดั้งเดิม (ที่เปิดใช้งานการตั้งค่าสถานะและส่วนขยาย. mjs) จัดการเส้นทางที่ไม่ใช่รูทและบัญชีสำหรับชื่อเส้นทางแบบเต็ม:

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

createDirectories('/components/widget/widget.js');คุณสามารถใช้มันเหมือน

และแน่นอนคุณอาจต้องการที่จะได้รับจินตนาการมากขึ้นโดยใช้สัญญากับ async / รอเพื่อยกระดับการสร้างไฟล์ในแบบซิงโครนัสที่อ่านได้ง่ายขึ้นเมื่อสร้างไดเรกทอรี แต่นั่นอยู่นอกเหนือขอบเขตของคำถาม


1
ทำไม const __dirname = path.resolve (); และไม่ใช้ชื่อ __ ในตัว?
TamusJRoyce

29

ในกรณีที่ผู้ใดสนใจรุ่นบรรทัดเดียว :)

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

กล่าวหา 1 ซับไม่จริง 1 บรรทัด
Hybrid web dev

20

คุณสามารถใช้mkdirและตรวจจับข้อผิดพลาดหากมีโฟลเดอร์อยู่
นี่คือ async (วิธีปฏิบัติที่ดีที่สุด) และปลอดภัย

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

(เลือกเพิ่มอาร์กิวเมนต์ที่สองด้วยโหมด)


ความคิดอื่น ๆ:

  1. คุณสามารถใช้ในขณะนั้นหรือรอโดยใช้การแนะนำแบบเนทีฟ

    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. คุณสามารถสร้างวิธีการสัญญาของคุณเองเช่น (ยังไม่ทดลอง):

    let mkdirAsync = (path, mode) => new Promise(
       (resolve, reject) => mkdir (path, mode, 
          err => (err && err.code !== 'EEXIST') ? reject(err) : resolve()
          )
       )
  3. สำหรับการตรวจสอบแบบซิงโครนัสคุณสามารถใช้:

    fs.existsSync(path) || fs.mkdirSync(path)
  4. หรือคุณสามารถใช้ห้องสมุดทั้งสองกำลังเป็นที่นิยมมากที่สุด

    • mkdirp (ทำโฟลเดอร์)
    • fsextra (supersets fs, เพิ่มเนื้อหาที่มีประโยชน์มากมาย)

1
สำหรับแนวทางที่มีแนวโน้ม # 1 คุณสามารถจัดการการจับอีกครั้ง mkdir('/path').catch(err => { if (err.code != 'EEXIST') throw err;}).then(myFunc);
อะไรจะเย็น

และใช้!==แทน!=
Quentin Roy

18

ด้วยแพ็กเกจfs-extraคุณสามารถทำได้โดยใช้สายการบินเดียว :

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

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

คำตอบที่ต่ำกว่ามาตรฐานเช่นนี้! fs-extra มี bacame ที่ต้องมีสำหรับฉัน ฉันคิดว่ามันเป็นความสามารถในการเขียน 10+ บรรทัด juste เพื่อตรวจสอบว่ามีโฟลเดอร์อยู่หรือไม่
538ROMEO

10

ทางออกที่ดีที่สุดที่จะใช้โมดูล NPM เรียกว่าโหนด-FS-พิเศษ มันมีวิธีการที่เรียกว่าmkdirซึ่งสร้างไดเรกทอรีที่คุณกล่าวถึง หากคุณให้เส้นทางไดเรกทอรียาวมันจะสร้างโฟลเดอร์หลักโดยอัตโนมัติ โมดูลเป็นชุดสุดยอดของโมดูล npm fsดังนั้นคุณสามารถใช้ฟังก์ชันทั้งหมดได้fsเช่นกันหากคุณเพิ่มโมดูลนี้


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

4
สำหรับ Node.js v7.4.0 สถานะเอกสารที่fs.exists()เลิกใช้แล้ว แต่fs.existsSync()ไม่ใช่ คุณสามารถเพิ่มลิงค์ไปยัง ressource ที่บอกว่าfs.existsSync()ถูกลดค่าใช้จ่ายได้หรือไม่?
รานซิส

1
คำตอบแบบรหัสเท่านั้นไม่มีประโยชน์กับผู้ใช้ที่มาที่คำถามนี้ในอนาคต โปรดแก้ไขคำตอบของคุณเพื่ออธิบายว่าทำไมรหัสของคุณถึงแก้ปัญหาเดิม
yivi

3
@francis, อืมฉันกำลังดู Node.js v5, nodejs.org/docs/latest-v5.x/api/fs.html#fs_fs_existssync_path
Ping.Goblue

1
ขอบคุณ! ดูเหมือนว่าฟังก์ชั่นที่มีอยู่ในรุ่น 0.12 ได้เลิกในรุ่น 4 และ 5 และได้รับการกู้คืนในรุ่น 6 และ 7 ... ชนิดของฟังก์ชั่นซอมบี้ ...
รานซิส

1
ใช่เห็นได้ชัดว่าตอนนี้ไม่ได้คัดค้าน ณ วันที่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");
    }

สิ่งนี้อาจช่วยคุณได้ :)


5

ENOENT: ไม่มีไฟล์หรือไดเรกทอรีดังกล่าว

สารละลาย

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
สิ่งนี้ใช้งานได้ดีขอบคุณ
Aljohn Yamaro

3

ฉันต้องการที่จะเพิ่ม refactor typescript สัญญาของคำตอบของ josh3736

มันทำสิ่งเดียวกันและมีกรณีขอบเหมือนกันมันเพิ่งเกิดขึ้นกับการใช้สัญญาพิมพ์ดีด typescript และทำงานกับ "ใช้เข้มงวด"

// 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

ด้วยโหนด 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

คุณสามารถใช้คำสั่งโหนดFile System fs.statเพื่อตรวจสอบว่ามี dir อยู่หรือไม่และfs.mkdirเพื่อสร้างไดเรกทอรีด้วยการติดต่อกลับหรือfs.mkdirSyncเพื่อสร้างไดเรกทอรีโดยไม่มีการเรียกกลับเช่นนี้:

//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

นี่เป็นฟังก์ชั่นเล็กน้อยในการสร้างไดเรกทอรีซ้ำ ๆ :

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

ใช้ async / คอย:

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

คุณจะต้องสัญญาfs:

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

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