18Oct

new_york_united_states_of_america_night_top_view_hdr_13629_2560x1440
Всем привет! Цель этого занятия – научиться работать с бинарными данными и с файловой системой. В Node.js для работы с файлами существует модуль fs, и в нем есть множество функций для самых различных операций с файлами и директориями.

Если мы приглядимся внимательно, то увидим первую особенность этого модуля. Почти все функции имеют два варианта, первое – просто имя, второе – имя со словом Sync. Слово Sync означает синхронно. Если я, например, вызову fs.readFile, то fs.readFile сначала прочитает полностью, а потом вызовет callback (fs.readFile(filename,[options], callback), а fs.readFileSync затормозит выполнение процесса Node.js, пока файл не будет прочитан. Поэтому, как правило, синхронный вызов используется либо в консольных утилитах, либо на стадии инициализации сервера, когда такие тормоза вполне допустимы, а асинхронный вызов в тех случаях, когда хочется, чтобы полноценно работал событийный цикл, то есть, чтобы Node.js не ждал, пока медленный диск сработает, и файл прочитается.

Посмотрим на реальный пример использования.  Создадим файла read.js. Подключаем модуль fs и вызываю асинхронную функцию readFile:

var fs = require('fs');  
  
fs.readFile(__filename, function(err, data) {  
  if (err) {  
    console.error(err);  
  } else {  
    console.log(data);  
  }  
});

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

var fs = require('fs');
  
var data = fs.readFileSync(__filename);  
fs.readFile(__filename, function(err, data) {  
  if (err) {  
    console.error(err);  
  } else {  
    console.log(data);  
  }  
});

При этом, в случае ошибки было бы исключение. Но мы здесь дальше будем работать с асинхронными вызовами. Итак, запускаем. Обратите внимание, вывелось не содержимое файла в виде строки, а специальный объект-буфер. Этот объект-буфер, который является высокоэффективным средством Node.js для работы с бинарными данными. Технически буфер – это непрерывная область памяти, которая, в данном случае, заполнена этими данными.

Работа с буфером достаточно похожа на работу со строкой. Здесь можно получить нулевой элемент:

var fs = require('fs');  
  
fs.readFile(__filename, function(err, data) {  
  if (err) {  
    console.error(err);  
  } else {  
    console.log(data); 
    data[0]
  }  
});

Можно получить длину буфера:

var fs = require('fs');  
  
fs.readFile(__filename, function(err, data) {  
  if (err) {  
    console.error(err);  
  } else {  
    console.log(data); 
    data.length
  }  
});

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

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

var fs = require('fs');

fs.readFile(__filename, function(err, data) {
    if (err) {
        console.error(err);
    } else {
        console.log(data.toString());
    }
});

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

fs.readFile(__filename, {encoding: 'utf-8'} function(err, data) {

Запускаем. Тоже работает. В этом случае преобразование в строку происходит непосредственно внутри функции readFile.

А теперь давайте посмотрим, что происходит, если где-то ошибка. Например, я считываю файл, которого не существует:

var fs = require('fs');

fs.readFile("blablabla", {encoding: 'utf-8'}, function(err, data) {
    if (err) {
        console.error(err);
    } else {
        console.log(data);
    }
});

Запускаю. Вывелась ошибка в console.error.

Обращаю ваше внимание, что в ошибке есть следующие данные. Во-первых, это имя ошибки. В данном случае, ‘ENOENT’ означает, что файла нет. Во-вторых, это цифровой код: error 34. Оба кода являются полностью cross-paltform, то есть, неважно под Windows, Linux или еще под чем-то я нахожусь, всегда если файл не найден, то это означает ошибка ‘ENOENT’. Соответственно, можем проверить, если код ENOENT, то среагирует одним способом, а иначе – сделает что-то еще:

var fs = require('fs');

fs.readFile("blablabla", {encoding: 'utf-8'}, function(err, data) {
    if (err) {
        if (err.code == 'ENOENT') {
            console.error(err.message);
        } else {
            console.error(err);
        }
    } else {
        console.log(data);
    }
});

Запускаем. Вот. Все сработало.

Если в будущем вас вдруг заинтересует, какие еще ошибки есть, или вы захотите получить расшифровку кода ошибки, то, к сожалению, в документации Node.js эта информация отсутствует. Но вы найдете ее в исходнике библиотеки libUV, в данном случае, в файле uv.h находятся различные виды ошибок, например, ENOENT или EACCES. Эти коды находятся именно здесь, потому что за ввод-вывод отвечает библиотека libUV. Она трансформирует различные коды операционных систем в cross-platform значения. Если мы заведомо знаем, что файл может не существовать, то можем проверить его наличие при помощи специального вызова. Для этого есть, во-первых, вызов fs.exists он проверяет, есть ли определенный путь, но он не может проверить, что это такое файл или директория. Для более точных проверок есть вызов  fs.stat и различные его варианты, которые вы можете более подробно изучить в документации.

Как правило, в большинстве ситуаций подходит просто stat. Он получает путь и возвращает объект специального типа  fs.Stats, который содержит подробную информацию о том, что по нему находится. Вот пример его использования:

var fs = require('fs');  
  
fs.stat(__filename, function(err, stats) {  
  console.log(stats.isFile());  
  console.log(stats);  
}); 

Запускаю. Console.log первый выявил true, потому что это файл, а второй вывел полную информацию о том, что находится по данному пути. Это немного зависит от операционной системы, файловой системы, но практически всегда есть размер size, а также mtime и дата создания ctime.

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

var fs = require('fs');  
  
fs.writeFile("file.tmp", "data", function(err) {  
  if (err) throw err;  
  
  fs.rename("file.tmp", "new.tmp", function(err) {  
    if (err) throw err;  
  
    fs.unlink("new.tmp", function(err) {  
      if (err) throw err;  
    });  
  });

А после переименовывания удаляем. Обратите внимание, что в каждом callback мы проверяем ошибку. То есть, после того, как файл создан, мы обязательно проверяем, если есть ошибка, нужно ее как-то обработать. Самый простейший способ – это throw. Дальше, переименовали, обработали. В каждом callback должна быть предусмотрена выработка ошибок, потому что ошибки могут быть в самых непредсказуемых местах. Если пропустить, то потом может быть совершенно непонятно, почему оно перестало работать, и где эту самую ошибку искать.

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

Код урока доступен здесь.

1-new-york-city-skyline-tribute-in-lights-and-lower-manhattan-at-night-nyc-jon-holiday

Материалы статьи взяты из данного скринкаста.

We are looking forward to meeting you on our website soshace.com

Стиль кода

1) Название переменных и методов должно быть четким и ясным, не экономьте на буквах.
2) Методы должны быть не больше 30-40 строк, методы решают одну конкретную задачу, плохие методы делают все.

23. Уроки Node.js. Домены, “асинхронный try..catch”. Часть 3.

Итак, из чего состоит app.js?

Он состоит из того, что мы с самого начала делаем домен, и запускаем в нем все наше приложение. Здесь подключаются все модули, создается сервер и т.д. Почему мы подключаем модули здесь? Потому что во время подключения модулей могут подключаться какие-то другие модули, а те могут подключать – третьи, и т.д.

2 Replies to “18. Уроки Node.js. Работа с Файлами, Модуль fs”

  1. Подскажите, а можно ли взять вторую строку в переменную, и собственно эту же строку удалить из файла?

  2. SmartEnd present 6 years ago

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

Leave a Reply