Bagaimana cara membuat path lengkap dengan node's fs.mkdirSync?


159

Saya mencoba membuat jalur lengkap jika tidak ada.

Kode ini terlihat seperti ini:

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest); 

Kode ini berfungsi dengan baik selama hanya ada satu subdirektori (sebuah newDest seperti 'dir1') namun ketika ada path direktori seperti ('dir1 / dir2') gagal dengan Error: ENOENT, tidak ada file atau direktori tersebut

Saya ingin dapat membuat path lengkap dengan beberapa baris kode yang diperlukan.

Saya membaca ada opsi rekursif pada fs dan mencobanya seperti ini

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest,'0777', true);

Saya merasa itu semudah itu untuk secara rekursif membuat direktori yang tidak ada. Apakah saya melewatkan sesuatu atau saya perlu mengurai path dan memeriksa setiap direktori dan membuatnya jika belum ada?

Saya cukup baru di Node. Mungkin saya menggunakan FS versi lama?


1
github.com/substack/node-mkdirp dan segala macam solusi lain pada pencarian Google ini .
jfriend00

4
@AndyRay Pertanyaan StackOverflow ini sekarang adalah hasil teratas di google untuk pertanyaan ini, yang lucu karena itu berarti berulang ...
Matt Parkins

1
Itu adalah masalah pada versi Node yang lebih lama, memperbarui ke Node 12+ memecahkan masalah
MrJomp

Jawaban:


48

Salah satu opsi adalah menggunakan modul shelljs

npm pasang shelljs

var shell = require('shelljs');
shell.mkdir('-p', fullPath);

Dari halaman itu:

Pilihan yang tersedia:

p: path lengkap (akan membuat dir menengah jika perlu)

Seperti yang telah dicatat orang lain, ada modul lain yang lebih fokus. Tetapi, di luar mkdirp, ia memiliki banyak operasi shell berguna lainnya (seperti itu, grep dll ...) dan ia bekerja pada windows dan * nix


2
Terima kasih! Saya akhirnya menggunakan exec (saya sudah menggunakan ini) dan itu bekerja seperti pesona. var exec = membutuhkan ('child_process'). exec; perintah var = "mkdir -p '" + newDest + "'"; opsi var = {}; var after = function (error, stdout, stderr) {console.log ('error', error); console.log ('stdout', stdout); console.log ('stderr', stderr); } exec (perintah, opsi, setelah);
David Silva Smith

24
Opsi ini dapat rusak pada platform node.js yang tidak memiliki instance mkdir baris perintah (yaitu, host non-Linux-y) sehingga tidak portabel, jika itu penting.
cshotton

1
@cshotton - apakah Anda mengacu pada komentar atau jawabannya? shelljs bekerja bahkan di windows. exec mkdir -p (komentar) tentu saja tidak.
bryanmac

Anda dapat menggunakan fungsi keren ini dengan Janji atau panggilan balik pilihan Anda.
Илья Зеленько

1
ini bukan solusi, ini adalah alternatif dari solusi. konteks: pics.onsizzle.com/...
Nika Kasradze

413

Edit

Versi NodeJS 10.12.0telah menambahkan dukungan asli untuk keduanya mkdirdan mkdirSyncuntuk membuat direktori secara rekursif dengan recursive: trueopsi sebagai berikut:

fs.mkdirSync(targetDir, { recursive: true });

Dan jika Anda suka fs Promises API, Anda bisa menulis

fs.promises.mkdir(targetDir, { recursive: true });

Jawaban Asli

Buat direktori secara rekursif jika tidak ada! ( Nol dependensi )

const fs = require('fs');
const path = require('path');

function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
  const sep = path.sep;
  const initDir = path.isAbsolute(targetDir) ? sep : '';
  const baseDir = isRelativeToScript ? __dirname : '.';

  return targetDir.split(sep).reduce((parentDir, childDir) => {
    const curDir = path.resolve(baseDir, parentDir, childDir);
    try {
      fs.mkdirSync(curDir);
    } catch (err) {
      if (err.code === 'EEXIST') { // curDir already exists!
        return curDir;
      }

      // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
      if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure.
        throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
      }

      const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
      if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) {
        throw err; // Throw if it's just the last created dir.
      }
    }

    return curDir;
  }, initDir);
}

Pemakaian

// Default, make directories relative to current working directory.
mkDirByPathSync('path/to/dir');

// Make directories relative to the current script.
mkDirByPathSync('path/to/dir', {isRelativeToScript: true});

// Make directories with an absolute path.
mkDirByPathSync('/path/to/dir');

Demo

Cobalah!

Penjelasan

  • [UPDATE] Solusi ini menangani kesalahan spesifik platform seperti EISDIRuntuk Mac dan EPERMdanEACCES untuk Windows. Terima kasih untuk semua komentar pelaporan oleh @PediT., @JohnQ, @ deed02392, @robyoder dan @Almenon.
  • Solusi ini menangani relatif dan absolut jalur . Terima kasih pada @john komentar.
  • Dalam hal jalur relatif, direktori target akan dibuat (diselesaikan) di direktori kerja saat ini. Untuk mengatasinya relatif terhadap dir skrip saat ini, sampaikan{isRelativeToScript: true} .
  • Menggunakan path.sepdan path.resolve(), bukan hanya /penggabungan, untuk menghindari masalah lintas platform.
  • Menggunakan fs.mkdirSyncdan menangani kesalahan dengan try/catchjika dilempar untuk menangani kondisi balapan: proses lain dapat menambahkan file antara panggilan ke fs.existsSync()dan fs.mkdirSync()dan menyebabkan pengecualian.
    • Cara lain untuk mencapainya adalah dengan memeriksa apakah ada file, lalu membuatnya, yaitu if (!fs.existsSync(curDir) fs.mkdirSync(curDir);,. Tapi ini adalah anti-pola yang membuat kode rentan terhadap kondisi balapan. Berkat komentar @GershomMaes tentang cek keberadaan direktori.
  • Membutuhkan Node v6 dan yang lebih baru untuk mendukung perusakan. (Jika Anda memiliki masalah dalam mengimplementasikan solusi ini dengan versi Node lama, beri saya komentar)

7
Suara positif untuk respons rekursif yang mudah dan tidak memerlukan perpustakaan atau pendekatan tambahan!
MikingTheViking

1
Hilang memerlukan pernyataan: const fs = require ('fs'); const path = butuhkan ('path');
Christopher Bull

1
@ChristopherBull, sengaja tidak ditambahkan hanya untuk fokus pada logika, tapi bagaimanapun, saya menambahkannya. Terima kasih;)
Mouneer

1
12 baris kode solid, nol dependensi, saya akan menerimanya setiap waktu.
moodboom

1
@ Mouneer pada Mac OS X 10.12.6, kesalahan yang dilemparkan ketika mencoba membuat "/" setelah melewati jalur absolut adalah "EISDIR" (Kesalahan: EISDIR: operasi ilegal pada direktori, mkdir '/'). Saya pikir mungkin memeriksa keberadaan dir masih cara lintas-platform terbaik untuk pergi (mengakui itu akan lebih lambat).
John Q

78

Jawaban yang lebih kuat adalah menggunakan use mkdirp .

var mkdirp = require('mkdirp');

mkdirp('/path/to/dir', function (err) {
    if (err) console.error(err)
    else console.log('dir created')
});

Kemudian lanjutkan untuk menulis file ke path lengkap dengan:

fs.writeFile ('/path/to/dir/file.dat'....

Pilih jawaban ini karena Anda mengimpor apa yang Anda butuhkan, bukan seluruh perpustakaan
Juan Mendes

1
Selamat atas lencana populis ;-)
janos

1
Terima kasih. Ini adalah metode terbaik.
Stepan Rafael


48

fs-extra menambahkan metode sistem file yang tidak termasuk dalam modul fs asli. Ini adalah pengganti pengganti fs.

Install fs-extra

$ npm install --save fs-extra

const fs = require("fs-extra");
// Make sure the output directory is there.
fs.ensureDirSync(newDest);

Ada opsi sinkronisasi dan asinkron.

https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md


5
Ini jawaban terbaik! Sebagian besar dari kita sudah memiliki fs-extra di aplikasi.
pagep

Ini akan bagus jika menawarkan kemungkinan untuk digunakan memfsuntuk pengujian unit. Itu tidak :-( github.com/jprichardson/node-fs-extra/issues/274
schnatterer

31

Menggunakan mengurangi kita dapat memverifikasi jika setiap jalur ada dan membuatnya jika perlu, juga dengan cara ini saya pikir lebih mudah untuk diikuti. Diedit, terima kasih @Arvin, kita harus menggunakan path.sep untuk mendapatkan pemisah segmen jalur khusus platform yang tepat.

const path = require('path');

// Path separators could change depending on the platform
const pathToCreate = 'path/to/dir'; 
pathToCreate
 .split(path.sep)
 .reduce((prevPath, folder) => {
   const currentPath = path.join(prevPath, folder, path.sep);
   if (!fs.existsSync(currentPath)){
     fs.mkdirSync(currentPath);
   }
   return currentPath;
 }, '');

4
Saat memberikan jawaban, lebih baik memberi penjelasan mengapa MENGAPA jawaban Anda adalah jawabannya .
Stephen Rauch

Maaf, Anda benar, saya pikir cara ini lebih bersih dan lebih mudah diikuti
josebui

4
@ josebui Saya pikir lebih baik menggunakan "path.sep" daripada mem-forward slash (/) untuk menghindari masalah spesifik lingkungan.
Arvin

solusi yang baik karena tidak memerlukan simpul> = 10 seperti jawaban lain
Karim

29

Fitur ini telah ditambahkan ke node.js dalam versi 10.12.0, jadi semudah meneruskan opsi {recursive: true}sebagai argumen kedua ke fs.mkdir()panggilan. Lihat contoh di dokumen resmi .

Tidak perlu modul eksternal atau implementasi Anda sendiri.


1
Saya menemukan permintaan tarik terkait github.com/nodejs/node/pull/23313
nurettin

1
Ini akan membuang kesalahan ketika direktori ada dan berhenti. Gunakan try catch block dapat membuatnya terus membuat folder lain yang tidak ada.
Choco Li

1
Ini harus menjadi jawaban yang diterima. Itu tidak melempar jika direktori sudah ada, dan dapat digunakan dengan async / menunggu melalui fs.promises.mkdir.
Rich Apodaca

7

saya tahu ini adalah pertanyaan lama, tetapi nodejs v10.12.0 sekarang mendukung ini secara native dengan recursiveopsi disetel ke true. fs.mkdir

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


2

Contoh untuk Windows (tidak ada ketergantungan tambahan dan penanganan kesalahan)

const path = require('path');
const fs = require('fs');

let dir = "C:\\temp\\dir1\\dir2\\dir3";

function createDirRecursively(dir) {
    if (!fs.existsSync(dir)) {        
        createDirRecursively(path.join(dir, ".."));
        fs.mkdirSync(dir);
    }
}

createDirRecursively(dir); //creates dir1\dir2\dir3 in C:\temp

2

Anda cukup memeriksa folder ada atau tidak di jalur secara rekursif dan membuat folder saat Anda memeriksa apakah mereka tidak ada. ( TANPA PERPUSTAKAAN EKSTERNAL )

function checkAndCreateDestinationPath (fileDestination) {
    const dirPath = fileDestination.split('/');
    dirPath.forEach((element, index) => {
        if(!fs.existsSync(dirPath.slice(0, index + 1).join('/'))){
            fs.mkdirSync(dirPath.slice(0, index + 1).join('/')); 
        }
    });
}

2

Anda dapat menggunakan fungsi selanjutnya

const recursiveUpload = (path: string) => {const paths = path.split ("/")

const fullPath = paths.reduce((accumulator, current) => {
  fs.mkdirSync(accumulator)
  return `${accumulator}/${current}`
  })

  fs.mkdirSync(fullPath)

  return fullPath
}

Jadi apa fungsinya:

  1. Buat pathsvariabel, tempat ia menyimpan setiap jalur dengan sendirinya sebagai elemen array.
  2. Menambahkan "/" di akhir setiap elemen dalam array.
  3. Membuat siklus:
    1. Membuat direktori dari rangkaian elemen array yang indeksnya dari 0 hingga iterasi saat ini. Pada dasarnya, ini bersifat rekursif.

Semoga itu bisa membantu!

Omong-omong, di Node v10.12.0 Anda bisa menggunakan pembuatan jalur rekursif dengan memberikannya sebagai argumen tambahan.

fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { if (err) throw err; });

https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_options


1

Terlalu banyak jawaban, tapi inilah solusi tanpa rekursi yang berfungsi dengan memisahkan jalan dan kemudian kiri-ke-kanan membangunnya kembali

function mkdirRecursiveSync(path) {
    let paths = path.split(path.delimiter);
    let fullPath = '';
    paths.forEach((path) => {

        if (fullPath === '') {
            fullPath = path;
        } else {
            fullPath = fullPath + '/' + path;
        }

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

Bagi mereka yang khawatir tentang kompatibilitas windows vs Linux, cukup ganti garis miring dengan double backslash '\' di kedua kejadian di atas tetapi TBH kita berbicara tentang node fs bukan windows command line dan yang sebelumnya cukup memaafkan dan kode di atas hanya akan bekerja pada Windows dan lebih merupakan solusi lintas platform yang lengkap.


file pada windows ditangani dengan backslash bukan forward slash. Kode Anda tidak akan berfungsi di sana. C: \ data \ test ...
DDD

Diedit tetapi menyarankan Anda memvalidasi komentar Anda. Pada node coba yang berikut dan lihat apa yang terjadi var fs = memerlukan ('fs') fs.mkdirSync ('test') fs.mkdirSync ('test \\ test1') fs.mkdirSync ('test / test2')
Hamiora

Apa pun yang Anda katakan .., suara turun saya masih tetap sampai Anda belajar menulis kode yang lebih baik.
DDD

Ha ha. Oke, saya akan bekerja sangat keras untuk belajar bagaimana menulis kode yang lebih baik. BTW sebagian besar jawaban di atas, termasuk OP, gunakan garis miring. Sarankan Anda berhenti trolling.
Hamiora

1
path.sepakan datang sebagai salah satu atau \\ untuk saya. path.delimiteradalah: atau;
Josh Anderson Slate

1
const fs = require('fs');

try {
    fs.mkdirSync(path, { recursive: true });
} catch (error) {
    // this make script keep running, even when folder already exist
    console.log(error);
}

0

Cara asinkron untuk membuat direktori secara rekursif:

import fs from 'fs'

const mkdirRecursive = function(path, callback) {
  let controlledPaths = []
  let paths = path.split(
    '/' // Put each path in an array
  ).filter(
    p => p != '.' // Skip root path indicator (.)
  ).reduce((memo, item) => {
    // Previous item prepended to each item so we preserve realpaths
    const prevItem = memo.length > 0 ? memo.join('/').replace(/\.\//g, '')+'/' : ''
    controlledPaths.push('./'+prevItem+item)
    return [...memo, './'+prevItem+item]
  }, []).map(dir => {
    fs.mkdir(dir, err => {
      if (err && err.code != 'EEXIST') throw err
      // Delete created directory (or skipped) from controlledPath
      controlledPaths.splice(controlledPaths.indexOf(dir), 1)
      if (controlledPaths.length === 0) {
        return callback()
      }
    })
  })
}

// Usage
mkdirRecursive('./photos/recent', () => {
  console.log('Directories created succesfully!')
})

0

Inilah versi imperatif saya mkdirpuntuk nodejs.

function mkdirSyncP(location) {
    let normalizedPath = path.normalize(location);
    let parsedPathObj = path.parse(normalizedPath);
    let curDir = parsedPathObj.root;
    let folders = parsedPathObj.dir.split(path.sep);
    folders.push(parsedPathObj.base);
    for(let part of folders) {
        curDir = path.join(curDir, part);
        if (!fs.existsSync(curDir)) {
            fs.mkdirSync(curDir);
        }
    }
}

0

Bagaimana dengan pendekatan ini:

if (!fs.existsSync(pathToFile)) {
            var dirName = "";
            var filePathSplit = pathToFile.split('/');
            for (var index = 0; index < filePathSplit.length; index++) {
                dirName += filePathSplit[index]+'/';
                if (!fs.existsSync(dirName))
                    fs.mkdirSync(dirName);
            }
        }

Ini berfungsi untuk jalur relatif.


0

Berdasarkan jawaban nol-dependensi mouneer , berikut adalah Typescriptvarian ramah yang sedikit lebih pemula , sebagai modul:

import * as fs from 'fs';
import * as path from 'path';

/**
* Recursively creates directories until `targetDir` is valid.
* @param targetDir target directory path to be created recursively.
* @param isRelative is the provided `targetDir` a relative path?
*/
export function mkdirRecursiveSync(targetDir: string, isRelative = false) {
    const sep = path.sep;
    const initDir = path.isAbsolute(targetDir) ? sep : '';
    const baseDir = isRelative ? __dirname : '.';

    targetDir.split(sep).reduce((prevDirPath, dirToCreate) => {
        const curDirPathToCreate = path.resolve(baseDir, prevDirPath, dirToCreate);
        try {
            fs.mkdirSync(curDirPathToCreate);
        } catch (err) {
            if (err.code !== 'EEXIST') {
                throw err;
            }
            // caught EEXIST error if curDirPathToCreate already existed (not a problem for us).
        }

        return curDirPathToCreate; // becomes prevDirPath on next call to reduce
    }, initDir);
}

0

Sebersih ini :)

function makedir(fullpath) {
  let destination_split = fullpath.replace('/', '\\').split('\\')
  let path_builder = destination_split[0]
  $.each(destination_split, function (i, path_segment) {
    if (i < 1) return true
    path_builder += '\\' + path_segment
    if (!fs.existsSync(path_builder)) {
      fs.mkdirSync(path_builder)
    }
  })
}

0

Saya punya masalah dengan opsi rekursif dari fs.mkdir jadi saya membuat fungsi yang melakukan hal berikut:

  1. Membuat daftar semua direktori, mulai dengan target akhir dir dan bekerja sampai ke induk root.
  2. Membuat daftar direktori baru yang diperlukan agar fungsi mkdir berfungsi
  3. Membuat setiap direktori dibutuhkan, termasuk final

    function createDirectoryIfNotExistsRecursive(dirname) {
        return new Promise((resolve, reject) => {
           const fs = require('fs');
    
           var slash = '/';
    
           // backward slashes for windows
           if(require('os').platform() === 'win32') {
              slash = '\\';
           }
           // initialize directories with final directory
           var directories_backwards = [dirname];
           var minimize_dir = dirname;
           while (minimize_dir = minimize_dir.substring(0, minimize_dir.lastIndexOf(slash))) {
              directories_backwards.push(minimize_dir);
           }
    
           var directories_needed = [];
    
           //stop on first directory found
           for(const d in directories_backwards) {
              if(!(fs.existsSync(directories_backwards[d]))) {
                 directories_needed.push(directories_backwards[d]);
              } else {
                 break;
              }
           }
    
           //no directories missing
           if(!directories_needed.length) {
              return resolve();
           }
    
           // make all directories in ascending order
           var directories_forwards = directories_needed.reverse();
    
           for(const d in directories_forwards) {
              fs.mkdirSync(directories_forwards[d]);
           }
    
           return resolve();
        });
     }

-1

Exec bisa berantakan di windows. Ada solusi yang lebih "nodie". Pada dasarnya, Anda memiliki panggilan rekursif untuk melihat apakah direktori ada dan menyelam ke anak (jika memang ada) atau membuatnya. Berikut adalah fungsi yang akan membuat anak-anak dan memanggil fungsi saat selesai:

fs = require('fs');
makedirs = function(path, func) {
 var pth = path.replace(/['\\]+/g, '/');
 var els = pth.split('/');
 var all = "";
 (function insertOne() {
   var el = els.splice(0, 1)[0];
   if (!fs.existsSync(all + el)) {
    fs.mkdirSync(all + el);
   }
   all += el + "/";
   if (els.length == 0) {
    func();
   } else {
     insertOne();
   }
   })();

}


-1

Versi ini bekerja lebih baik pada Windows daripada jawaban teratas karena mengerti keduanya /dan path.sepagar garis miring bekerja pada Windows sebagaimana mestinya. Mendukung jalur absolut dan relatif (relatif terhadap process.cwd).

/**
 * Creates a folder and if necessary, parent folders also. Returns true
 * if any folders were created. Understands both '/' and path.sep as 
 * path separators. Doesn't try to create folders that already exist,
 * which could cause a permissions error. Gracefully handles the race 
 * condition if two processes are creating a folder. Throws on error.
 * @param targetDir Name of folder to create
 */
export function mkdirSyncRecursive(targetDir) {
  if (!fs.existsSync(targetDir)) {
    for (var i = targetDir.length-2; i >= 0; i--) {
      if (targetDir.charAt(i) == '/' || targetDir.charAt(i) == path.sep) {
        mkdirSyncRecursive(targetDir.slice(0, i));
        break;
      }
    }
    try {
      fs.mkdirSync(targetDir);
      return true;
    } catch (err) {
      if (err.code !== 'EEXIST') throw err;
    }
  }
  return false;
}

Apakah downvote untuk mendukung Windows dengan benar? Apakah saya menyebutkan itu berfungsi pada OS lain juga?
Qwertie
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.