Node js файлы в папке. NodeJS

Всем привет! В этой статье мы рассмотрим, как записывать и читать файлы в NodeJS .

Платформа NodeJS позволяет записывать и читать файлы в операционной системе. Для этого нам потребуется использовать модуль FS (file system ).

Var fs = require("fs");

Для демонстрации считывания содержимого файлов давайте создадим файлик с названием readme.txt .

// содержимое файла readme.txt
Здесь какое-нибудь содержимое файла

Var text = fs.readFileSync("readme.txt", "utf8");
console.log(text);

Мы используем метод, в который передаем первым параметром имя файла, а вторым – кодировку. Как понятно из названия, этот метод является синхронным. Это значит, что весь код, который идет ниже, выполнится только тогда, когда весь файл будет прочитан. Дальше мы просто записываем полученные данные в переменную, которую потом выводим на экран.

Теперь давайте попробуем считанное нами содержание файла записать в новый файл. Для этого напишем следующее:

Fs.writeFileSync("writeme.txt", text);

Теперь после запуска кода вы увидите, что создался новый файлик с названием writeme.txt , в котором будет содержимое, записанное в переменную text из файла readme.txt .

Давайте рассмотрим, как использовать методы асинхронно. Например, считаем файлик readme.txt :


console.log(data);
});

Console.log("выведется раньше, чем данные из файла");

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

Теперь давайте снова считаем содержимое файла readme.txt и запишем его в файл writeme.txt , но только теперь асинхронно.

Fs.readFile("readme.txt", "utf8", function(error, data) {
fs.writeFile("writeme.txt", data);
});

А на этом у меня сегодня все. Спасибо за внимание!

Последнее обновление: 23.05.2019

Для работы с файлами в Node.js предназначен модуль . Рассмотрим, как с ним работать.

Чтение из файла

Допустим, в одной папке с файлом приложения app.js расположен текстовый файл hello.txt с простейшим текстом, например:

Hello Node JS!

Для чтения файла в синхронном варианте применяется функция fs.readFileSync() :

Let fileContent = fs.readFileSync("hello.txt", "utf8");

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

Для асинхронного чтения файла применяется функция fs.readFile :

Fs.readFile("hello.txt", "utf8", function(error,data){ });

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

Для чтения файла определим в файле app.js следующий код:

Const fs = require("fs"); // асинхронное чтение fs.readFile("hello.txt", "utf8", function(error,data){ console.log("Асинхронное чтение файла"); if(error) throw error; // если возникла ошибка console.log(data); // выводим считанные данные }); // синхронное чтение console.log("Синхронное чтение файла") let fileContent = fs.readFileSync("hello.txt", "utf8"); console.log(fileContent);

И здесь стоит обратить внимание, что несмотря на то, что функция fs.readFile() вызывается первой, но так как она асинхронная, она не блокирует поток выполнения, поэтому ее результат выводится в самом конце.

Запись файла

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

Fs.writeFileSync("hello.txt", "Привет ми ми ми!")

Также для записи файла можно использовать асинхронную функцию fs.writeFile() , которая принимает те же параметры:

Fs.writeFile("hello.txt", "Привет МИГ-29!")

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

Const fs = require("fs"); fs.writeFile("hello.txt", "Hello мир!", function(error){ if(error) throw error; // если возникла ошибка console.log("Асинхронная запись файла завершена. Содержимое файла:"); let data = fs.readFileSync("hello.txt", "utf8"); console.log(data); // выводим считанные данные });

Следует отметить, что эти методы полностью перезаписывают файл. Если надо дозаписать файл, то применяются методы fs.appendFile()/fs.appendFileSync() :

Const fs = require("fs"); fs.appendFileSync("hello.txt", "Привет ми ми ми!"); fs.appendFile("hello.txt", "Привет МИД!", function(error){ if(error) throw error; // если возникла ошибка console.log("Запись файла завершена. Содержимое файла:"); let data = fs.readFileSync("hello.txt", "utf8"); console.log(data); // выводим считанные данные });

Удаление файла

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

Fs.unlinkSync("hello.txt")

Также для удаления файла можно использовать асинхронную функцию fs.unlink() , которая принимает путь к файлу и функцию, вызываемую при завершении удаления:

Fs.unlink("hello.txt", (err) => { if (err) console.log(err); // если возникла ошибка else console.log("hello.txt was deleted"); });

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

Работа с файловыми дескрипторами в 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 , предназначенный для работы с путями к файлам. Перед использованием этого модуля в программе его надо подключить:

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

Если у вас есть путь к файлу, то, используя возможности модуля 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").dirname("/test/something") // "" require("path").dirname("/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, но большинство из них охватывают либо какие-то конкретные варианты использования, либо темы, применимые уже тогда, когда у вас есть работающий Node.js То тут, то там я вижу комментарии вроде «я скачал Node.js, что теперь?». Статья ответит на этот вопрос и объяснит, как начать с самого начала.

Что есть Node.js?

Много путаницы у новичков в Node.js возникает из-за непонимания того, что же на самом деле это такое. И описание на nodejs.org не слишком помогает разобраться.

Важно понять, что Node - это не веб-сервер. Сам по себе он ничего не делает. Это не Apache. Там нет конфиг-файла, в котором указывается путь до HTML-файлов. Если вам нужен HTTP-сервер, вам нужно написать HTTP-сервер (с помощью встроенных библиотек). Node.js - это просто ещё один способ выполнять код на вашем компьютере. Это просто среда для выполнения JavaScript.

Установка Node

Установить Node.js очень просто. Если вы используете Windows или Mac, установочные файлы доступны на странице загрузки .

Я установил Node, что теперь?

Сразу после установки вам становится доступна новая команда node . Её можно использовать двумя разными способами. Первый способ - без аргументов. Откроется интерактивная оболочка (REPL: read-eval-print-loop), где вы можете выполнять обычный JavaScript-код.

$ node > console .log("Hello World" ); Hello World undefined

В примере выше я написал console.log("Hello World") в оболочке и нажал Enter. Node.js выполнит этот код, и мы увидим сообщение. undefined после него выводится потому, что оболочка отображает возвращаемое значение каждой команды, а console.log ничего не возвращает.

Кроме того, мы можем передать Node файл с JavaScript для выполнения. Именно так вы и будете практически всегда делать.

hello.js

console .log("Hello World" );

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

$ node hello.js Hello World

В этом примере я переместил console.log в файл, который затем передал команде node в качестве аргумента. Node затем запускает JavaScript из этого файла и выводит Hello World .

Делаем что-нибудь полезное - работа с файлами

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

example_log.txt

2013-08-09T13 :50 :33.166Z A 2 2013-08-09T13 :51 :33.166Z B 1 2013-08-09T13 :52 :33.166Z C 6 2013-08-09T13 :53 :33.166Z B 8 2013-08-09T13 :54 :33.166Z B 5

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

my_parser.js

var fs = require ("fs" ); function (err, logData ) { // Если произошла ошибка, то мы генерируем исключение, // и работа приложения завершается if (err) throw err; // logData имеет тип Buffer, переводим в string var text = logData.toString(); });

Работать с файлами в Node.js очень просто благодаря встроенному модулю файловой системы fs . Этот модуль содержит функцию readFile , принимающую в качестве аргументов путь до файла и коллбэк. Коллбэк вызовется, когда завершится чтение файла. Данные из файла поступают в виде объекта типа Buffer , по сути представляющего собой массив байтов. Мы можем перевести его в строку с помощью функции toString ().

Теперь давайте займёмся обработкой логов. Это вполне себе обычный код JavaScript, поэтому я не буду вдаваться в детали.

my_parser.js

var fs = require ("fs" ); // Считывание содержимого файла в память fs.readFile("example_log.txt" , function (err, logData ) { // Если произошла ошибка, то генерируем исключение, // и работа приложения завершится. if (err) throw err; // logData имеет тип Buffer, переводим в строку var text = logData.toString(); var results = {}; // Разбивка файла по строкам var lines = text.split("\n" ); lines.forEach(function (line ) { var parts = line.split(" " ); var letter = parts; var count = parseInt (parts); if (!results) { results = 0 ; } results += parseInt (count); }); console .log(results); // { A: 2, B: 14, C: 6 } });

Теперь, когда вы передадите этот файл node в качестве аргумента, он выведет результат и завершит работу.

$ node my_parser.js { A: 2 , B: 14 , C: 6 }

Я часто использую Node.js для таких задач. Это простая и мощная альтернатива bash-скриптам.

Асинхронные коллбэки

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

Это особенно важно для веб-серверов. Доступ к базам данных в современных веб-приложениях - обычное дело. Пока вы ждёте ответа от базы, Node.js может обработать ещё запросы. Это позволяет вам обрабатывать тысячи одновременных соединений с очень маленькими затратами, сравнимыми с созданием отдельного потока для каждого соединения.

Делаем что-нибудь полезное - HTTP-сервер

Как я уже говорил ранее, Node.js не делает ничего «из коробки». Один из встроенных модулей позволяет без особых усилий создать простой HTTP-сервер , указанный в примере на сайте Node.js .

my_web_server.js

var http = require ("http" ); http.createServer(function (req, res ) { res.writeHead(200 , {"Content-Type" : "text/plain" }); res.end("Hello World\n" ); }).listen(8080 ); console .log("Server running on port 8080." );

Когда я говорю, «простой», это значит «простой». Это не навороченный HTTP-сервер. Он не работает с HTML или изображениями. Фактически, что бы вы ни запросили, он вернёт Hello World . Тем не менее, можете запустить его, зайти на http://localhost:8080 в браузере и увидеть этот текст.

$ node my_web_server.js

Возможно, вы заметите небольшую разницу: ваше приложение не завершает работу. Так происходит потому, что вы создали сервер, и теперь он будет продолжать работать и отвечать на запросы до тех пор, пока вы не убьёте node сами.

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

Если вы хотите сделать веб-сервер на Node.js или просто какое-то приложение, тогда вам нужно уметь читать файлы.

Node предоставляет библиотеку для работы с файловой системой. К примеру, для чтения файлов.

Асинхронное чтение файла (неблокирующее)

"Нормальный" способ чтения файлов в Node.js это чтение асинхронным способом. Это значит, что вы вызываете команду чтения файла и передаете callback, который будет вызван при завершении чтения. Это позволяет работать с несколькими запросами чтения параллельно.

Для этого мы можем использовать метод readFile из класса fs .

examples/node/non-blocking-read-file.js

Var fs = require("fs"); fs.readFile("DATA", "utf8", function(err, contents) { console.log(contents); }); console.log("after calling readFile");

Для начала мы загружаем класс fs с помощью команды require . Затем вызываем метод readFile , который получает 3 параметра: имя файла ("DATA" в нашем случае), кодировку файла ("utf8" в примере) и функцию. Эта функция будет вызывана, когда завершится операция чтения файла. Функция получит два параметра. Первый - информация о каких-либо ошибках, второй - содержимое файла.

Как только программа будет запущена, Node начнет читать файл в фоновом режиме, но продолжит выполнение. Таким образом, сначала будет выполнен вызов console.log("after calling readFile"); , который выведет этот текст в консоль. Затем, когда содержимое файла будет загружено в память, Node вызовет функцию, которую мы передали в метод readFile , и она выведет в консоль содержимое файла.

Синхронное чтение файла (блокирующее)

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

Для чтения файлов вы можете использовать метод readFileSync из класса fs :

examples/node/blocking-read-file.js

Var fs = require("fs"); var contents = fs.readFileSync("DATA", "utf8"); console.log(contents);