Создание файла только в том случае, если он не существует в Node.js
У нас есть буфер, который мы хотели бы записать в файл. Если файл уже существует, нам нужно увеличить индекс на нем и повторить попытку. Есть ли способ создать файл только в том случае, если он не существует, или я должен просто ставить файлы, пока не получу ошибку, чтобы найти тот, который еще не существует?
Например, у меня есть файлы a_1.jpg
и a_2.jpg
. Я хочу, чтобы мой метод попытался создать a_1.jpg
и a_2.jpg
, и завершился сбой, и, наконец, успешно создал a_3.jpg
.
Идеальный метод будет выглядеть примерно так:
fs.writeFile(path, data, { overwrite: false }, function (err) {
if (err) throw err;
console.log('It\ saved!');
});
или вот так:
fs.createWriteStream(path, { overwrite: false });
Есть ли что-нибудь подобное в библиотеке node fs
?
EDIT: Мой вопрос не в том, есть ли отдельная функция, которая проверяет существование. Это так: есть ли способ создать файл, если он не существует, в одном системном вызове?
Ответы
Ответ 1
Как правильно догадалась ваша интуиция, наивное решение с помощью пары вызовов exists / writeFile
неверно. Асинхронный код работает непредсказуемым образом. И в данном случае это
- Есть ли файл
a.txt
? - Нет.
- (Файл
a.txt
создается другой программой)
- Напишите
a.txt
, если это возможно. - Хорошо.
Но да, мы можем сделать это за один раз. Мы работаем с файловой системой, поэтому рекомендуется прочитать инструкцию по эксплуатации на fs
. И эй, здесь интересная часть.
'w' - открыть файл для записи. Файл создается (если он не существует) или усечено (если оно существует).
'wx' - Как и 'w', но не работает, если существует путь.
Итак, все, что нам нужно сделать, это просто добавить wx
в вызов fs.open
. Но, эй, нам не нравится fopen
-like IO. Давайте немного почитаем fs.writeFile
.
fs.readFile(имя файла [, опции], обратный вызов) #
filename String
options Объект
кодировка String | Null default = null
flag String default = 'r'
функция обратного вызова
Это options.flag
выглядит многообещающим. Поэтому мы стараемся
fs.writeFile(path, data, { flag: 'wx' }, function (err) {
if (err) throw err;
console.log("It saved!");
});
И он отлично работает для одной записи. Я предполагаю, что этот код не сработает еще более причудливыми способами, но если вы попытаетесь решить свою задачу с ним. У вас есть атомарная проверка на существование a_#.jpg
и запись там, если она пуста ", но все остальные состояния fs
не заблокированы, а файл a_1.jpg
может спонтанно исчезнуть, пока вы уже проверяете a_5.jpg
, Большинство файловых систем * не являются ACID, и тот факт, что вы можете выполнять хотя бы некоторые атомные операции, чудесен. Очень вероятно, что код wx
не будет работать на какой-либо платформе. Поэтому, ради вашего здравомыслия, используйте базу данных, наконец.
Дополнительная информация о страданиях
Представьте, что мы пишем что-то вроде memoize-fs
, которое кэширует результаты вызовов функций в файловой системе, чтобы сэкономить нам время сети/процессора, Можем ли мы открыть файл для чтения, если он существует, и для записи, если это не так, все в одном вызове? Позвольте взглянуть на эти флаги смешно. Через некоторое время умственные упражнения мы видим, что a+
делает то, что мы хотим: если файл не существует, он создает один и открывает его как для чтения, так и для записи, и если файл существует, он делает это, не очищая файл (как w+
). Но теперь мы не можем использовать его ни в (smth)File
, ни в функциях create(Smth)Stream
. И это кажется недостающей особенностью.
Поэтому не стесняйтесь записывать его как запрос функции (или даже ошибку) в Node.js github, так как отсутствие асинхронной API асинхронной файловой системы является недостатком Node. Хотя не ожидает изменений в ближайшее время.
Ответ 2
Что об использовании a
вариант?
Согласно документам:
'a+' - Открыть файл для чтения и добавления. Файл создается, если он не существует.
Кажется, отлично работает с createWriteStream
Ответ 3
Этот метод больше не рекомендуется. fs.exists устарела. Смотрите комментарии.
Вот несколько вариантов:
1) Иметь 2 "фс" звонка. Первый - это вызов " fs.exists ", а второй - " fs.write/read и т.д."
//checks if the file exists.
//If it does, it just calls back.
//If it doesn't, then the file is created.
function checkForFile(fileName,callback)
{
fs.exists(fileName, function (exists) {
if(exists)
{
callback();
}else
{
fs.writeFile(fileName, {flag: 'wx'}, function (err, data)
{
callback();
})
}
});
}
function writeToFile()
{
checkForFile("file.dat",function()
{
//It is now safe to write/read to file.dat
fs.readFile("file.dat", function (err,data)
{
//do stuff
});
});
}
2) Или сначала создайте пустой файл:
--- Синхронизация:
//If you want to force the file to be empty then you want to use the 'w' flag:
var fd = fs.openSync(filepath, 'w');
//That will truncate the file if it exists and create it if it doesn't.
//Wrap it in an fs.closeSync call if you don't need the file descriptor it returns.
fs.closeSync(fs.openSync(filepath, 'w'));
--- ASync:
var fs = require("fs");
fs.open(path, "wx", function (err, fd) {
// handle error
fs.close(fd, function (err) {
// handle error
});
});
3) Или используйте " touch ": https://github.com/isaacs/node-touch
Ответ 4
В этом случае в одном системном вызове вы можете использовать модуль fs-extra
npm.
После этого будет создан файл, а также каталог, в который он будет помещен.
const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFile(file, err => {
console.log(err) // => null
});
Другой способ - использовать secureFileSync, который будет делать то же самое, но синхронно.
const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFileSync(file)
Ответ 5
Вы можете сделать что-то вроде этого:
function writeFile(i){
var i = i || 0;
var fileName = 'a_' + i + '.jpg';
fs.exists(fileName, function (exists) {
if(exists){
writeFile(++i);
} else {
fs.writeFile(fileName);
}
});
}