Как в node js найти файл

You can easily read directories and sub-directories to get all files in Node.js but sometimes you might need to find files by their names, extensions, or a pattern in NodeJS. It might sound difficult but fortunately, it’s not.

You can easily search into a directory (folder) with Node.js for a specific file or files. For that, you can use the filename, file extension, or a regular expression.

In this article, I will walk you through all the methods to find files in a directory and its sub-directories with pure JavaScript and an external package.

You can search for files in Node.js using the following methods:

  • First, you have to read the directory using readdir() method from the Node fs module.
  • Loop over the files returned from the readdir() method.
  • Find the extension of each file using path.extname() method and check if it matches the extension that you are searching for.
  • You can find the file name with the path.parse() method of each file and compare it with the provided name.
  • There is an external package called glob to find files using a pattern like a regular expression.

Let’s see all the methods in detail step by step.

How to Find Files By Extension, Name or Pattern in Node.js

Find Files By Extension in Node.js

To get files by using extensions, I have created a function called findByExtension() with two parameters. The first parameter is the directory name and the second parameter is the file extension.

First, we have to get all the files from the provided directory using readdir() method. You can import this method from the fs module which is a Node.js core module. This method will return all the files and sub-directories available inside the targeted folder.

In the next step, you have to loop over the files you got from the readdir() method and check which file matches your target extension type.

          const { readdir } = require('fs/promises');
const path = require('path');

const findByExtension = async (dir, ext) => {
    const matchedFiles = [];

    const files = await readdir(dir);

    for (const file of files) {
        // Method 1:
        const fileExt = path.extname(file);

        if (fileExt === `.${ext}`) {
            matchedFiles.push(file);
        }
    }

    return matchedFiles;
};
        

In the above code, you can see I am doing the same. Getting all the files and looping over them using for...of loop. Now, you can check the extension type in two ways:

  • Method 1: Get the extension from each file with path.extname() method and match it with the target extension type.
  • Method 2: You can compare the target extension with every file name using endsWith() string method.

Note: The readdir() method returns a promise therefore, you have to use async and await to handle that promise.

In the above example, I have used the first method. For any file that matches our target extension, I am pushing that file name in the matchedFiles array. Finally, I return this array at the end of the function.

Method 1:

          findByExtension('./uploads', 'js').then((files) => {
    console.log(files);
});

// [ "home.js", "main.js"]
        

Now, I am calling the findByExtension() function with a directory name and extension type. Because it is an async function, I have to use then to get all the returned array. You can use a self-executing function in javascript If you don’t want to call the function manually.

Method 2:

          const { readdir } = require('fs/promises');

const findByExtension = async (dir, ext) => {
    const matchedFiles = [];

    const files = await readdir(dir);

    for (const file of files) {
        // Method 2:
        if (file.endsWith(`.${ext}`)) {
            matchedFiles.push(file);
        }
    }

    return matchedFiles;
};
        

In this method, I am checking if the file name ends with the target extension type using the endsWith() method. If this method returns true, I will push the file name into the matchedFiles array.

Note: You can only get matching file from the upload directory. If there are sub-directories inside upload directory and you also want to check files from those directories then we have a complete guide to get files recursively from a directory using NodeJS.

Search Files By Name in Node.js

To search for files by name, you have to follow the same process to get all the files from a directory. Then you can loop over those files to check if the file name matches the name you are looking for.

The only difference, in this case, is how we compare the file names. You can compare those names in 3 ways:

  • Method 1: Get the file name of each file using path.parse() method. Then compare it with the provided name using the === comparison operator.
  • Method 2: Check if the file name starts with the provided name using the startsWith() method in javascript.
  • Method 3: Check if the file name includes the provided name with the includes() method in javascript.

Let’s learn about all these methods with examples.

Method 1:

          const path = require('path');
const { readdir } = require('fs/promises');

const findByName = async (dir, name) => {
    const matchedFiles = [];

    const files = await readdir(dir);

    for (const file of files) {
        // Method 1:
        const filename = path.parse(file).name;

        if (filename === name) {
            matchedFiles.push(file);
        }
    }

    return matchedFiles;
};
        

You can get the filename of each file inside the for loop using path.parse() method. Then you can check if this name is equal (===) to the name you are looking for.

One thing you should keep in mind while using this technique is that you have to provide the exact filename. It can not check for partially matching names.

          findByName('./uploads', 'home').then((files) => {
    console.log(files);
});

// [ "home.js", "home.css"]
        

But if you don’t want to use exact names while searching, you can use the other two methods.

Also Read: Easy Ways to Download Any File From Node.js Server Using Express

Method 2:

          const { readdir } = require('fs/promises');

const findByName = async (dir, name) => {
    const matchedFiles = [];

    const files = await readdir(dir);

    for (const file of files) {
        // Method 2:
        if (file.startsWith(name)) {
            matchedFiles.push(file);
        }
    }

    return matchedFiles;
};
        

In this method, I am checking if the file starts with the provided name with the startsWith() method. With this method, you can find all the files that start with a piece of string.

You don’t need the exact name of a file. You have to provide part from the start.

          findByName('./uploads', 'ho').then((files) => {
    console.log(files);
});

// [ "home.js", "home.css"]
        

If you want to find files with partially matching names from any part of the file, you can use our next method.

Method 3:

          const { readdir } = require('fs/promises');
const path = require('path');

const findByName = async (dir, name) => {
    const matchedFiles = [];

    const files = await readdir(dir);

    for (const file of files) {
        // Method 3:
        if (file.includes(name)) {
            matchedFiles.push(file);
        }
    }

    return matchedFiles;
};
        

Instead of checking if the file starts with a piece of string, you can check if the file includes that string with the includes() method. In this way, you will get all the files that contain your search query.

          findByName('./uploads', 'ome').then((files) => {
    console.log(files);
});

// [ "home.js", "home.css"]
        

You can see when I am calling the findByName() function with a random string in the second parameter, it is returning files that contain the string.

Get Files By Matching Pattern Using Glob in Node.js

So far we have seen how to find files using javascript. We wrote functions according to our requirements. If you have a complex directory structure with many directories and sub-directories, it will be a good idea to use an external package.

Now I will show you how you can use the glob package to search for files. With this package, you can find files from multiple directories and sub-directories using some pattern like a regular expression.

First, initialize a Node.js project by creating the package.json file in the root of your project folder. You have to create this file using the following command:

          npm init -y
        

In the next step, you have to install the glob package in your project. You can install this package with this command:

          npm install --save glob
        

Now you can use this package in your project. I have created a file called glob.js with the following code in it. You can give this file any name you want.

The glob() function takes 2 parameters. The first parameter is the pattern that it will use to find files and the second parameter is the callback function.

In the callback function, you get 2 things i.e. error and matched files. You can check if there is any error and you will get the files that match the pattern.

          // glob.js file

const glob = require('glob');

glob('uploads/*.js', (err, files) => {
    if (err) {
        console.log(err);
        return;
    }

    console.log(files);
});
        

In this example, I am calling the glob() function with the «uploads/*.js» pattern. It will return all files that end with .js inside the uploads directory. But it will not return files from any sub-directories present inside the uploads directory.

If you want to get all files from a directory including its sub-directories, you have to use a different pattern. There are different types of patterns that you can use with the glob package.

Also Read: Perfect Ways to Serve Static Files in Express and Node.js

List of Common Glob Patterns

You can create a complex pattern for your search if you need to use different characters. You can learn more about those by following the official documentation of this package.

I will show you some frequently used patterns that you can use in your project. These patterns are:

  • *.extension — This pattern will return all the files present in the current directory. For example: «*.js«, «*.png«, «*.json«.
  • directory/*.extension — This pattern will return files present in the given directory without the sub-directories. For example: «uploads/*.js«, «uploads/*.css«, «images/*.jpg«.
  • directory/**/*.extension — This pattern will give all files from the given directory including the files from its sub-directories. For example: «uploads/**/*.js«, «uploads/**/*.css«, «images/**/*.jpg«.
  • **/*.extension — It will give all the files from the current directory and its subdirectories. For example: «**/*.js«, «**/*.html«, «**/*.json«.
  • */**/*.extension — If you want to find files only from sub-directories present in the current directory, you can use this pattern. For example: «*/**/*.js«, «*/**/*.html«, «*/**/*.json«

Conclusion

Knowing how to work with files is an important skill for every developer. You can get all files from a directory or you can search any specific types of files in Nodejs. This is what you have learned in this article.

I have shown you different methods to find files from a directory and sub-directories. You can use filenames, extensions, or any pattern to identify file types.

Use fs and path modules to get your files or you can install the glob package to apply patterns. I tried to give you everything related to this topic. I hope you will be able to find files in Node.js by using names, extensions, or patterns in your project.

You can use OS help for this. Here is a cross-platform solution:

1. The bellow function uses ls and dir and does not search recursively but it has relative paths

var exec = require('child_process').exec;
function findFiles(folder,extension,cb){
    var command = "";
    if(/^win/.test(process.platform)){
        command = "dir /B "+folder+"\*."+extension;
    }else{
        command = "ls -1 "+folder+"/*."+extension;
    }
    exec(command,function(err,stdout,stderr){
        if(err)
            return cb(err,null);
        //get rid of r from windows
        stdout = stdout.replace(/r/g,"");
        var files = stdout.split("n");
        //remove last entry because it is empty
        files.splice(-1,1);
        cb(err,files);
    });
}

findFiles("folderName","html",function(err,files){
    console.log("files:",files);
})

2. The bellow function uses find and dir, searches recursively but on windows it has absolute paths

var exec = require('child_process').exec;
function findFiles(folder,extension,cb){
    var command = "";
    if(/^win/.test(process.platform)){
        command = "dir /B /s "+folder+"\*."+extension;
    }else{
        command = 'find '+folder+' -name "*.'+extension+'"'
    }
    exec(command,function(err,stdout,stderr){
        if(err)
            return cb(err,null);
        //get rid of r from windows
        stdout = stdout.replace(/r/g,"");
        var files = stdout.split("n");
        //remove last entry because it is empty
        files.splice(-1,1);
        cb(err,files);
    });
}

findFiles("folder","html",function(err,files){
    console.log("files:",files);
})

Сегодня, в девятой части перевода руководства по Node.js, мы поговорим о работе с файлами. В частности, речь пойдёт о модулях fs и path — о файловых дескрипторах, о путях к файлам, о получении информации о файлах, об их чтении и записи, о работе с директориями.

[Советуем почитать] Другие части цикла

Часть 1: Общие сведения и начало работы
Часть 2: JavaScript, V8, некоторые приёмы разработки
Часть 3: Хостинг, REPL, работа с консолью, модули
Часть 4: npm, файлы package.json и package-lock.json
Часть 5: npm и npx
Часть 6: цикл событий, стек вызовов, таймеры
Часть 7: асинхронное программирование
Часть 8: Руководство по Node.js, часть 8: протоколы HTTP и WebSocket
Часть 9: Руководство по Node.js, часть 9: работа с файловой системой
Часть 10: Руководство по Node.js, часть 10: стандартные модули, потоки, базы данных, NODE_ENV
Полная PDF-версия руководства по Node.js

Работа с файловыми дескрипторами в Node.js

Прежде чем вы сможете взаимодействовать с файлами, находящимися в файловой системе вашего сервера, вам необходимо получить дескриптор файла.

Дескриптор можно получить, воспользовавшись для открытия файла асинхронным методом open() из модуля fs:

const fs = require('fs')
fs.open('/Users/flavio/test.txt', 'r', (err, fd) => {
  //fd - это дескриптор файла
})

Обратите внимание на второй параметр, r, использованный при вызове метода fs.open(). Это — флаг, который сообщает системе о том, что файл открывают для чтения. Вот ещё некоторые флаги, которые часто используются при работе с этим и некоторыми другими методами:

  • r+ — открыть файл для чтения и для записи.
  • w+ — открыть файл для чтения и для записи, установив указатель потока в начало файла. Если файл не существует — он создаётся.
  • a — открыть файл для записи, установив указатель потока в конец файла. Если файл не существует — он создаётся.
  • a+ — открыть файл для чтения и записи, установив указатель потока в конец файла. Если файл не существует — он создаётся.

Файлы можно открывать и пользуясь синхронным методом fs.openSync(), который, вместо того, чтобы предоставить дескриптор файла в коллбэке, возвращает его:

const fs = require('fs')
try {
  const fd = fs.openSync('/Users/flavio/test.txt', 'r')
} catch (err) {
  console.error(err)
}

После получения дескриптора любым из вышеописанных способов вы можете производить с ним необходимые операции.

Данные о файлах

С каждым файлом связан набор данных о нём, исследовать эти данные можно средствами Node.js. В частности, сделать это можно, используя метод stat() из модуля fs.

Вызывают этот метод, передавая ему путь к файлу, и, после того, как Node.js получит необходимые сведения о файле, он вызовет коллбэк, переданный методу stat(). Вот как это выглядит:

const fs = require('fs')
fs.stat('/Users/flavio/test.txt', (err, stats) => {
  if (err) {
    console.error(err)
    return
  }
  //сведения о файле содержатся в аргументе `stats`
})

В Node.js имеется возможность синхронного получения сведений о файлах. При таком подходе главный поток блокируется до получения свойств файла:

const fs = require('fs')
try {
  const stats = fs.statSync ('/Users/flavio/test.txt')
} catch (err) {
  console.error(err)
}

Информация о файле попадёт в константу stats. Что это за информация? На самом деле, соответствующий объект предоставляет нам большое количество полезных свойств и методов:

  • Методы .isFile() и .isDirectory() позволяют, соответственно, узнать, является ли исследуемый файл обычным файлом или директорией.
  • Метод .isSymbolicLink() позволяет узнать, является ли файл символической ссылкой.
  • Размер файла можно узнать, воспользовавшись свойством .size.

Тут имеются и другие методы, но эти — самые употребимые. Вот как ими пользоваться:

const fs = require('fs')
fs.stat('/Users/flavio/test.txt', (err, stats) => {
  if (err) {
    console.error(err)
    return
  }
  stats.isFile() //true
  stats.isDirectory() //false
  stats.isSymbolicLink() //false
  stats.size //1024000 //= 1MB
})

Пути к файлам в Node.js и модуль path

Путь к файлу — это адрес того места в файловой системе, где он расположен.

В Linux и macOS путь может выглядеть так:

/users/flavio/file.txt

В Windows пути выглядят немного иначе:

C:usersflaviofile.txt

На различия в форматах записи путей при использовании разных операционных систем следует обращать внимание, учитывая операционную систему, используемую для развёртывания Node.js-сервера.

В Node.js есть стандартный модуль path, предназначенный для работы с путями к файлам. Перед использованием этого модуля в программе его надо подключить:

const path = require('path')

▍Получение информации о пути к файлу

Если у вас есть путь к файлу, то, используя возможности модуля path, вы можете, в удобном для восприятия и дальнейшей обработки виде, узнать подробности об этом пути. Выглядит это так:

const notes = '/users/flavio/notes.txt'
path.dirname(notes) // /users/flavio
path.basename(notes) // notes.txt
path.extname(notes) // .txt

Здесь, в строке notes, хранится путь к файлу. Для разбора пути использованы следующие методы модуля path:

  • dirname() — возвращает родительскую директорию файла.
  • basename() — возвращает имя файла.
  • extname() — возвращает расширение файла.

Узнать имя файла без расширения можно, вызвав метод .basename() и передав ему второй аргумент, представляющий расширение:

path.basename(notes, path.extname(notes)) //notes

▍Работа с путями к файлам

Несколько частей пути можно объединить, используя метод path.join():

const name = 'flavio'
path.join('/', 'users', name, 'notes.txt') //'/users/flavio/notes.txt'

Найти абсолютный путь к файлу на основе относительного пути к нему можно с использованием метода path.resolve():

path.resolve('flavio.txt') 
//'/Users/flavio/flavio.txt' при запуске из моей домашней папки

В данном случае Node.js просто добавляет /flavio.txt к пути, ведущем к текущей рабочей директории. Если при вызове этого метода передать ещё один параметр, представляющий путь к папке, метод использует его в качестве базы для определения абсолютного пути:

path.resolve('tmp', 'flavio.txt')
// '/Users/flavio/tmp/flavio.txt' при запуске из моей домашней папки

Если путь, переданный в качестве первого параметра, начинается с косой черты — это означает, что он представляет собой абсолютный путь.

path.resolve('/etc', 'flavio.txt')
// '/etc/flavio.txt'

Вот ещё один полезный метод — path.normalize(). Он позволяет найти реальный путь к файлу, используя путь, в котором содержатся спецификаторы относительного пути вроде точки (.), двух точек (..), или двух косых черт:

path.normalize('/users/flavio/..//test.txt') 
// /users/test.txt

Методы resolve() и normalize() не проверяют существование директории. Они просто находят путь, основываясь на переданным им данным.

Чтение файлов в Node.js

Самый простой способ чтения файлов в Node.js заключается в использовании метода fs.readFile() с передачей ему пути к файлу и коллбэка, который будет вызван с передачей ему данных файла (или объекта ошибки):

fs.readFile('/Users/flavio/test.txt', (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data)
})

Если надо, можно воспользоваться синхронной версией этого метода — fs.readFileSync():

const fs = require('fs')
try {
  const data = fs.readFileSync('/Users/flavio/test.txt')
  console.log(data)
} catch (err) {
  console.error(err)
}

По умолчанию при чтении файлов используется кодировка utf8, но кодировку можно задать и самостоятельно, передав методу соответствующий параметр.

Методы fs.readFile() и fs.readFileSync() считывают в память всё содержимое файла. Это означает, что работа с большими файлами с применением этих методов серьёзно отразится на потреблении памяти вашим приложением и окажет влияние на его производительность. Если с такими файлами нужно работать, лучше всего воспользоваться потоками.

Запись файлов в Node.js

В Node.js легче всего записывать файлы с использованием метода fs.writeFile():

const fs = require('fs')
const content = 'Some content!'
fs.writeFile('/Users/flavio/test.txt', content, (err) => {
  if (err) {
    console.error(err)
    return
  }
  //файл записан успешно
})

Есть и синхронная версия того же метода — fs.writeFileSync():

const fs = require('fs')
const content = 'Some content!'
try {
  const data = fs.writeFileSync('/Users/flavio/test.txt', content)
  //файл записан успешно
} catch (err) {
  console.error(err)
}

Эти методы, по умолчанию, заменяют содержимое существующих файлов. Изменить их стандартное поведение можно, воспользовавшись соответствующим флагом:

fs.writeFile('/Users/flavio/test.txt', content, { flag: 'a+' }, (err) => {})

Тут могут использоваться флаги, которые мы уже перечисляли в разделе, посвящённом дескрипторам. Подробности о флагах можно узнать здесь.

Присоединение данных к файлу

Метод fs.appendFile() (и его синхронную версию — fs.appendFileSync()) удобно использовать для присоединения данных к концу файла:

const content = 'Some content!'
fs.appendFile('file.log', content, (err) => {
  if (err) {
    console.error(err)
    return
  }
  //готово!
})

Об использовании потоков

Выше мы описывали методы, которые, выполняя запись в файл, пишут в него весь объём переданных им данных, после чего, если используются их синхронные версии, возвращают управление программе, а если применяются асинхронные версии — вызывают коллбэки. Если вас такое состояние дел не устраивает — лучше будет воспользоваться потоками.

Работа с директориями в Node.js

Модуль fs предоставляет в распоряжение разработчика много удобных методов, которые можно использовать для работы с директориями.

▍Проверка существования папки

Для того чтобы проверить, существует ли директория и может ли Node.js получить к ней доступ, учитывая разрешения, можно использовать метод fs.access().

▍Создание новой папки

Для того чтобы создавать новые папки, можно воспользоваться методами fs.mkdir() и fs.mkdirSync():

const fs = require('fs')
const folderName = '/Users/flavio/test'
try {
  if (!fs.existsSync(dir)){
    fs.mkdirSync(dir)
  }
} catch (err) {
  console.error(err)
}

▍Чтение содержимого папки

Для того чтобы прочесть содержимое папки, можно воспользоваться методами fs.readdir() и fs.readdirSync(). В этом примере осуществляется чтение содержимого папки — то есть — сведений о том, какие файлы и поддиректории в ней имеются, и возврат их относительных путей:

const fs = require('fs')
const path = require('path')
const folderPath = '/Users/flavio'
fs.readdirSync(folderPath)

Вот так можно получить полный путь к файлу:

fs.readdirSync(folderPath).map(fileName => {
  return path.join(folderPath, fileName)
}

Результаты можно отфильтровать для того, чтобы получить только файлы и исключить из вывода директории:

const isFile = fileName => {
  return fs.lstatSync(fileName).isFile()
}
fs.readdirSync(folderPath).map(fileName => {
  return path.join(folderPath, fileName)).filter(isFile)
}

▍Переименование папки

Для переименования папки можно воспользоваться методами fs.rename() и fs.renameSync(). Первый параметр — это текущий путь к папке, второй — новый:

const fs = require('fs')
fs.rename('/Users/flavio', '/Users/roger', (err) => {
  if (err) {
    console.error(err)
    return
  }
  //готово
})

Переименовать папку можно и с помощью синхронного метода fs.renameSync():

const fs = require('fs')
try {
  fs.renameSync('/Users/flavio', '/Users/roger')
} catch (err) {
  console.error(err)
}

▍Удаление папки

Для того чтобы удалить папку, можно воспользоваться методами fs.rmdir() или fs.rmdirSync(). Надо отметить, что удаление папки, в которой что-то есть, задача несколько более сложная, чем удаление пустой папки. Если вам нужно удалять такие папки, воспользуйтесь пакетом fs-extra, который весьма популярен и хорошо поддерживается. Он представляет собой замену модуля fs, расширяющую его возможности.

Метод remove() из пакета fs-extra умеет удалять папки, в которых уже что-то есть.

Установить этот модуль можно так:

npm install fs-extra

Вот пример его использования:

const fs = require('fs-extra')
const folder = '/Users/flavio'
fs.remove(folder, err => {
  console.error(err)
})

Его методами можно пользоваться в виде промисов:

fs.remove(folder).then(() => {
  //готово
}).catch(err => {
  console.error(err)
})

Допустимо и применение конструкции async/await:

async function removeFolder(folder) {
  try {
    await fs.remove(folder)
    //готово
  } catch (err) {
    console.error(err)
  }
}
const folder = '/Users/flavio'
removeFolder(folder)

Модуль fs

Выше мы уже сталкивались с некоторыми методами модуля fs, применяемыми при работе с файловой системой. На самом деле, он содержит ещё много полезного. Напомним, что он не нуждается в установке, для того, чтобы воспользоваться им в программе, его достаточно подключить:

const fs = require('fs')

После этого у вас будет доступ к его методам, среди которых отметим следующие, некоторые из которых вам уже знакомы:

  • fs.access(): проверяет существование файла и возможность доступа к нему с учётом разрешений.
  • fs.appendFile(): присоединяет данные к файлу. Если файл не существует — он будет создан.
  • fs.chmod(): изменяет разрешения для заданного файла. Похожие методы: fs.lchmod(), fs.fchmod().
  • fs.chown(): изменяет владельца и группу для заданного файла. Похожие методы: fs.fchown(), fs.lchown().
  • fs.close(): закрывает дескриптор файла.
  • fs.copyFile(): копирует файл.
  • fs.createReadStream(): создаёт поток чтения файла.
  • fs.createWriteStream(): создаёт поток записи файла.
  • fs.link(): создаёт новую жёсткую ссылку на файл.
  • fs.mkdir(): создаёт новую директорию.
  • fs.mkdtemp(): создаёт временную директорию.
  • fs.open(): открывает файл.
  • fs.readdir(): читает содержимое директории.
  • fs.readFile(): считывает содержимое файла. Похожий метод: fs.read().
  • fs.readlink(): считывает значение символической ссылки.
  • fs.realpath(): разрешает относительный путь к файлу, построенный с использованием символов . и .., в полный путь.
  • fs.rename(): переименовывает файл или папку.
  • fs.rmdir(): удаляет папку.
  • fs.stat(): возвращает сведения о файле. Похожие методы: fs.fstat(), fs.lstat().
  • fs.symlink(): создаёт новую символическую ссылку на файл.
  • fs.truncate(): обрезает файл до заданной длины. Похожий метод: fs.ftruncate().
  • fs.unlink(): удаляет файл или символическую ссылку.
  • fs.unwatchFile(): отключает наблюдение за изменениями файла.
  • fs.utimes(): изменяет временную отметку файла. Похожий метод: fs.futimes().
  • fs.watchFile(): включает наблюдение за изменениями файла. Похожий метод: fs.watch().
  • fs.writeFile(): записывает данные в файл. Похожий метод: fs.write().

Интересной особенностью модуля fs является тот факт, что все его методы, по умолчанию, являются асинхронными, но существуют и их синхронные версии, имена которых получаются путём добавления слова Sync к именам асинхронных методов.

Например:

  • fs.rename()
  • fs.renameSync()
  • fs.write()
  • fs.writeSync()

Использование синхронных методов серьёзно влияет на то, как работает программа.

В Node.js 10 имеется экспериментальная поддержка этих API, основанных на промисах.

Исследуем метод fs.rename(). Вот асинхронная версия этого метода, использующая коллбэки:

const fs = require('fs')
fs.rename('before.json', 'after.json', (err) => {
  if (err) {
    return console.error(err)
  }
  //готово
})

При использовании его синхронной версии для обработки ошибок используется конструкция try/catch:

const fs = require('fs')
try {
  fs.renameSync('before.json', 'after.json')
  //готово
} catch (err) {
  console.error(err)
}

Основное различие между этими вариантами использования данного метода заключается в том, что во втором случае выполнение скрипта будет заблокировано до завершения файловой операции.

Модуль path

Модуль path, о некоторых возможностях которого мы тоже уже говорили, содержит множество полезных инструментов, позволяющих взаимодействовать с файловой системой. Как уже было сказано, устанавливать его не нужно, так как он является частью Node.js. Для того чтобы пользоваться им, его достаточно подключить:

const path = require('path')

Свойство path.sep этого модуля предоставляет символ, использующийся для разделения сегментов пути ( в Windows и / в Linux и macOS), а свойство path.delimiter даёт символ, используемый для отделения друг от друга нескольких путей (; в Windows и : в Linux и macOS).

Рассмотрим и проиллюстрируем примерами некоторые методы модуля path.

▍path.basename()

Возвращает последний фрагмент пути. Передав второй параметр этому методу можно убрать расширение файла.

require('path').basename('/test/something') //something
require('path').basename('/test/something.txt') //something.txt
require('path').basename('/test/something.txt', '.txt') //something

▍path.dirname()

Возвращает ту часть пути, которая представляет имя директории:

require('path').dirname('/test/something') // /test
require('path').dirname('/test/something/file.txt') // /test/something

▍path.extname()

Возвращает ту часть пути, которая представляет расширение файла:

require('path').extname('/test/something') // ''
require('path').extname('/test/something/file.txt') // '.txt'

▍path.isAbsolute()

Возвращает истинное значение если путь является абсолютным:

require('path').isAbsolute('/test/something') // true
require('path').isAbsolute('./test/something') // false

▍path.join()

Соединяет несколько частей пути:

const name = 'flavio'
require('path').join('/', 'users', name, 'notes.txt') //'/users/flavio/notes.txt'

▍path.normalize()

Пытается выяснить реальный путь на основе пути, который содержит символы, использующиеся при построении относительных путей вроде ., .. и //:

require('path').normalize('/users/flavio/..//test.txt') ///users/test.txt

▍path.parse()

Преобразует путь в объект, свойства которого представляют отдельные части пути:

  • root: корневая директория.
  • dir: путь к файлу, начиная от корневой директории
  • base: имя файла и расширение.
  • name: имя файла.
  • ext: расширение файла.

Вот пример использования этого метода:

require('path').parse('/users/test.txt')

В результате его работы получается такой объект:

{
  root: '/',
  dir: '/users',
  base: 'test.txt',
  ext: '.txt',
  name: 'test'
}

▍path.relative()

Принимает, в качестве аргументов, 2 пути. Возвращает относительный путь из первого пути ко второму, основываясь на текущей рабочей директории:

require('path').relative('/Users/flavio', '/Users/flavio/test.txt') //'test.txt'
require('path').relative('/Users/flavio', '/Users/flavio/something/test.txt') //'something/test.txt'

▍path.resolve()

Находит абсолютный путь на основе переданного ему относительного пути:

path.resolve('flavio.txt') 
//'/Users/flavio/flavio.txt' при запуске из моей домашней папки.

Итоги

Сегодня мы рассмотрели модули Node.js fs и path, которые используются для работы с файловой системой. В следующей части этой серии, на которой она завершается, мы обсудим модули os, events, http, поговорим о работе с потоками и с системами управления базами данных в Node.js.

Уважаемые читатели! Какими npm-пакетами вы пользуетесь при работе с файловой системой в Node.js?

Привет! Как в Node.js найти файл(ы) (или проверить его/их существование), зная только часть его имени? :help:

Н-р, мы хотим узнать существует ли в директории «/path/» файл(ы) с именем, в котором присутствует строка «298AA5B8». :yes:

В зависимости от ситуации требуется вывести либо только самый последний файл, либо список всех файлов в названии которых есть указанная строка (отсортированный по времени). :thanks:

Допустим, в директории «/path/» имеются следующие файлы:

  • AJSDA484_LFK4883G_20049562.txt
  • AJSDA484_LFK4883G_20037853.txt
  • LFK4883G_AJSDA484_20037853.txt
  • 298AA5B8_AJSDA484_18477588.txt
  • LFK4883G_298AA5B8_93874223.txt

… может быть оооооочень много файлов :haha:

Предполагаемая реализация:

const fs = require("fs");
const string = '298AA5B8';

fs.findFile('/path/', string, function(error, result){
    if (error) {
        console.log("Файл не найден");
    } else {
        console.log(result);
        //298AA5B8_AJSDA484_18477588.txt — самый последний добавленный файл с разыскиваемой строкой в имени
    }
});

Важно чтобы функция сразу заканчивала свою работу как найдёт первое вхождение.

Если надо все файлы, где встречается строка в имени, то предполагаемая реализация такая:

const fs = require("fs");
const string = '298AA5B8';

fs.findFiles('/path/', string, function(error, result){
    if (error) {
        console.log("Файл не найден");
    } else {
        console.log(result);
        //Выведет массив файлов
        //['298AA5B8_AJSDA484_18477588.txt','LFK4883G_298AA5B8_93874223.txt'];

    }
});

В настоящий момент удалось найти только поиск по конкретному названию файла: :no:

const fs = require("fs");

fs.access("filename.txt", function(error){
    if (error) {
        console.log("Файл не найден");
    } else {
        console.log("Файл найден");
    }
});

или

const fs = require("fs");

fs.stat("filename.txt", function(err, stats) {
    if (err) {
        console.log("Файл не найден");
    } else {
        console.log("Файл найден");
    }
});

Существует ли в Node.js функциональность, чтобы найти файл/ы (или проверить их существование), зная только часть их имени? :D

P.S. аналогия здесь может быть с SQL:

"SELECT * FROM files WHERE param1 = 'string' OR param2 = 'string' ORDER BY time DESC LIMIT 1";

или для массива файлов:

"SELECT * FROM files WHERE param1 = 'string' OR param2 = 'string' ORDER BY time DESC";

Файлы¶

Для работы с файлами в Node.js используется встроенный модуль fs, который выполняет все синхронные и асинхронные операции ввода/вывода применительно к файлам. Чтение и запись файла могут осуществляться одним из двумя способов:

  • с использованием Buffer;
  • через создание соответствующего потока.

Чтение файлов и директорий¶

Для чтения файла в асинхронном режиме используется метод Node.js readFile(), который принимает три параметра:

  • путь к файлу;
  • кодировка;
  • callback-функция, вызываемая после получения содержимого файла.
fs.readFile('files/data.txt', 'utf8', (err, data) => {
    if (err) throw err;

    console.log(data);
});

Callback-функции передается два аргумента: ошибка и полученные данные в строковом формате. Если операция прошла успешна, то в качестве ошибки передается null.

Если в readFile() не указать кодировку, то данные файла будут возвращены в формате Buffer.

Поскольку метод выполняется асинхронно, то не происходит блокировки главного процесса Node.js. Но в некоторых случаях может понадобиться синхронное чтение файла, для этого есть метод readFileSync(), но при этом выполнение главного процесса будет заблокировано до тех пор, пока полностью не будет загружено содержимое файла.

const content = fs.readFileSync('files/data.txt', 'utf8');
console.log(content);

Node.js readFileSync() возвращает результат чтения файла и принимает два параметра:

  • путь к файлу;
  • кодировку.

Обработка и перехват ошибок при использовании readFileSync() осуществляется с помощью конструкции try{...}catch(){...}.

try {
    const content = fs.readFileSync(
        'files/data.txt',
        'utf8'
    );
    console.log(content);
} catch (e) {
    console.log(e);
}

Чтобы инициировать ошибку, укажите неправильный путь к файлу.

Методы readFile() и readFileSync() для работы с файлами используют Buffer. Но есть и другой способ считать содержимое файла: создать поток с помощью Node.js fs.createReadStream(). Любой поток в Node.js является экземпляром класса EventEmitter, который позволяет обрабатывать возникающие в потоке события.

Параметры, принимаемые fs.createReadStream():

  • путь к файлу;
  • объект со следующими настройками:
    • encoding — кодировка (по умолчанию utf8);
    • mode — режим доступа (по умолчанию 0o666);
    • autoClose — если true, то при событиях error и finish поток закроется автоматически (по умолчанию true).
const stream = fs.createReadStream(
    'files/data.txt',
    'utf8'
);
stream.on('data', (data) => console.log(data));
stream.on('error', (err) => console.log(`Err: ${err}`));

Вместо объекта настроек можно передать строку, которая будет задавать кодировку.

Использование потока имеет ряд преимуществ перед Buffer:

  • Меньшее использование памяти за счет чтения содержимого по частям;
  • Для объемных файлов время между запросом и ответом существенно сокращается за счет того, что данные начинают поступать по частям, а не после полной загрузки;
  • Возможность перенаправить данные в другой поток с помощью метода pipe().

Для чтения директорий используются методы readdir() и readdirSync(), для асинхронного и синхронного режимов соответственно.

Node.js readdir() работает асинхронно и принимает три аргумента:

  • путь к директории;
  • кодировку;
  • callback-функцию, которая принимает аргументами ошибку и массив файлов директории (при успешном выполнении операции ошибка передается как null).
fs.readdir('files', 'utf8', (err, files) => {
    if (err) throw err;

    console.log(files);
});

Node.js readdirSync() работает синхронно, возвращает массив найденных файлов и принимает два параметра:

  • путь к директории;
  • кодировку.
try {
    const files = fs.readdirSync('files', 'utf8');
    console.log(files);
} catch (e) {
    console.log(e);
}

Создание и запись файлов и директорий¶

В Node.js файлы могут быть записаны также синхронно и асинхронно. Для асинхронной записи имеется метод writeFile(), принимающий следующие аргументы:

  • путь к файлу;
  • данные для записи;
  • параметры записи:
    • кодировка (по умолчанию utf8);
    • права доступа (по умолчанию 0o666);
  • callback-функция, которая вызывается по завершению операции и единственным аргументом принимает ошибку (в случае успешной записи передается null).
fs.writeFile(
    'files/data.txt',
    'File Content',
    'utf8',
    (err) => {
        if (err) throw err;

        console.log('Done');
    }
);

Если нет необходимости указывать параметры записи, то третьим параметром Node.js writeFile() можно сразу передать callback-функцию.

Для синхронной записи Node.js файла используйте writeFileSync(). Метод принимает все те же аргументы, что и writeFile() за исключением callback-функции. В качестве значения возвращает undefined.

try {
    fs.writeFileSync(
        'files/data.txt',
        'File Content',
        'utf8'
    );
    console.log('Done');
} catch (e) {
    console.log(e);
}

Как и в случае с readFileSync() обработка ошибок происходит с помощью try{...}catch(){...}.

Методы writeFile() и writeFileSync() перезаписывают уже имеющуюся в файле информацию новыми данными. Если вам нужно внести новые данные без удаления старых, используйте методы appendFIle() и appendFileAsync(), которые имеют идентичные параметры.

fs.appendFile(
    'files/data.txt',
    'nFile Content 2',
    'utf8',
    (err) => {
        if (err) throw err;

        console.log('Done');
    }
);

Для записи файла через потока ввода имеется метод fs.createWriteStream(), который возвращает поток ввода и принимает два параметра:

  • путь к файлу;
  • объект со следующими настройками:
    • encoding — кодировка (по умолчанию utf8);
    • mode — режим доступа (по умолчанию 0o666);
    • autoClose — если true, то при событиях error и finish поток закроется автоматически (по умолчанию true).
const stream = fs.createWriteStream(
    'files/data.txt',
    'utf8'
);

stream.on('error', (err) => console.log(`Err: ${err}`));
stream.on('finish', () => console.log('Done'));

stream.write('First linen');
stream.write('Second linen');
stream.end();

Чтобы создать директорию, используйте методы mkdir() и mkdirSync().

Node.js mkdir() работает асинхронно и принимает в качестве параметров:

  • путь к директории;
  • объект со следующими настройками:
    • recursive — если true, создает директорию и все ее родительские директории согласно указанному пути, если они еще не существуют (по умолчанию false, т. е. все родительские директории уже должны быть созданы, иначе будет сгенерирована ошибка);
    • mode — режим доступа, параметр не поддерживается на ОС Windows (по умолчанию 0o777);
    • callback-функцию, которая единственным аргументом принимает ошибку, при успешном создании директории передается null.

Вторым параметром можно сразу передать callback-функцию.

fs.mkdir('files/dir/subdir', { recursive: true }, (err) => {
    if (err) throw err;

    console.log('Created');
});

Node.js mkdirSync() создает директорию синхронно и возвращает undefined. Обработка ошибок осуществляется через try{...}catch(){...}. Метод mkdirSync() принимает те же параметры, что и mkdir(), за исключением callback-функции.

try {
    fs.mkdirSync('files/dir/subdir', { recursive: true });
    console.log('Done');
} catch (e) {
    console.log(e);
}

Удаление файлов и директорий¶

Чтобы удалить в Node.js файлы используйте методы unlink() и unlinkSync().

Метод unlink() асинхронный и принимает имя файла, который нужно удалить, и callback-функцию с ошибкой в качестве параметра (null, если удаление прошло успешно).

fs.unlink('files/data.txt', (err) => {
    if (err) throw err;

    console.log('Deleted');
});

Для синхронного удаления файла используйте unlinkSync(), которому единственным аргументом передается имя файла.

try {
    fs.unlinkSync('files/data.txt');
    console.log('Deleted');
} catch (e) {
    console.log(e);
}

Для удаления директорий имеются методы rmdir() и rmdirSync() соответственно. Они полностью идентичны unlink() и unlinkSync(), только вместо имени файла принимают имя директории.

Пример rmdir().

fs.rmdir('files/dir', (err) => {
    if (err) throw err;

    console.log('Deleted');
});

Пример rmdirSync().

try {
    fs.rmdirSync('files/dir');
    console.log('Deleted');
} catch (e) {
    console.log(e);
}

existsSync()¶

Проверка наличия в Node.js файла или директории происходит с помощью метода existsSync(), который принимает путь к файлу или директории и возвращает либо true, либо false. Как вы понимаете, Node.Js existsSync() работает в синхронном режиме.

try {
    const exists = fs.existsSync('files');
    console.log('Exists: ', exists);
} catch (e) {
    console.log(e);
}

Раньше был и Node.js exists(), но сейчас он уже официально устарел и не поддерживается.

Понравилась статья? Поделить с друзьями:
  • Как найти существительные в казахском тексте
  • Как найти изображение альбома
  • Как найти площадь рулона обоев
  • Как найти вектор нормали через координаты
  • Как найти объем куба с ребром 6см