[英]Creating a file only if it doesn't exist in Node.js
We have a buffer we'd like to write to a file.我们有一个要写入文件的缓冲区。 If the file already exists, we need to increment an index on it, and try again.
如果文件已经存在,我们需要增加一个索引,然后再试一次。 Is there a way to create a file only if it doesn't exist, or should I just stat files until I get an error to find one that doesn't exist already?
有没有办法只在文件不存在的情况下创建文件,或者我应该只统计文件,直到出现错误以找到一个不存在的文件?
For example, I have files a_1.jpg
and a_2.jpg
.例如,我有文件
a_1.jpg
和a_2.jpg
。 I'd like my method to try creating a_1.jpg
and a_2.jpg
, and fail, and finally successfully create a_3.jpg
.我希望我的方法尝试创建
a_1.jpg
和a_2.jpg
并失败,最后成功创建a_3.jpg
。
The ideal method would look something like this:理想的方法如下所示:
fs.writeFile(path, data, { overwrite: false }, function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
or like this:或像这样:
fs.createWriteStream(path, { overwrite: false });
Does anything like this exist in node's fs
library? node的
fs
库中是否存在类似的东西?
EDIT : My question isn't if there's a separate function that checks for existence.编辑:我的问题不是是否有单独的 function 来检查是否存在。 It's this: is there a way to create a file if it doesn't exist, in a single file system call?
就是这样:如果文件不存在,有没有办法在单个文件系统调用中创建文件?
As your intuition correctly guessed, the naive solution with a pair of exists / writeFile
calls is wrong.正如您的直觉正确猜到的那样,带有一对
exists / writeFile
调用的天真解决方案是错误的。 Asynchronous code runs in unpredictable ways.异步代码以不可预测的方式运行。 And in given case it is
在给定的情况下,它是
a.txt
?a.txt
吗? — No. a.txt
gets created by another program) a.txt
由另一个程序创建)a.txt
if it's possible.a.txt
。 — Okay. But yes, we can do that in a single call.但是,是的,我们可以在一次调用中做到这一点。 We're working with file system so it's a good idea to read developer manual on
fs
.我们正在使用文件系统,因此阅读
fs
上的开发人员手册是个好主意。 And hey, here's an interesting part.嘿, 这是一个有趣的部分。
'w' - Open file for writing.
'w' - 打开文件进行写入。 The file is created (if it does not exist) or truncated (if it exists).
该文件被创建(如果它不存在)或被截断(如果它存在)。
'wx' - Like 'w' but fails if path exists.
'wx' - 类似于 'w' 但如果路径存在则失败。
So all we have to do is just add wx
to the fs.open
call.所以我们所要做的就是将
wx
添加到fs.open
调用中。 But hey, we don't like fopen
-like IO.但是,嘿,我们不喜欢
fopen
类的 IO。 Let's read on fs.writeFile
a bit more.让我们多读一点
fs.writeFile
。
fs.readFile(filename[, options], callback)#
fs.readFile(filename[, options], callback)#
filename String
文件名字符串
options Object
选项对象
encoding String |
编码字符串 | Null default = null
空默认值 = 空
flag String default = 'r'
标志字符串默认 = 'r'
callback Function
回调函数
That options.flag
looks promising. options.flag
看起来很有希望。 So we try所以我们尝试
fs.writeFile(path, data, { flag: 'wx' }, function (err) {
if (err) throw err;
console.log("It's saved!");
});
And it works perfectly for a single write.它非常适合单次写入。 I guess this code will fail in some more bizarre ways yet if you try to solve your task with it.
我猜如果你尝试用它来解决你的任务,这段代码会以一些更奇怪的方式失败。 You have an atomary "check for
a_#.jpg
existence, and write there if it's empty" operation, but all the other fs
state is not locked, and a_1.jpg
file may spontaneously disappear while you're already checking a_5.jpg
.您有一个原子“检查
a_#.jpg
是否存在,如果为空则写入”操作,但所有其他fs
状态都没有锁定,并且a_1.jpg
文件可能会在您已经检查a_5.jpg
自发消失。 Most * file systems are no ACID databases, and the fact that you're able to do at least some atomic operations is miraculous.大多数*文件系统都不是ACID数据库,而且您至少能够执行一些原子操作的事实是奇迹。 It's very likely that
wx
code won't work on some platform. wx
代码很可能在某些平台上不起作用。 So for the sake of your sanity, use database, finally .因此,为了您的理智,请使用数据库,最后。
Imagine we're writing something like memoize-fs
that caches results of function calls to the file system to save us some network/cpu time.想象一下,我们正在编写类似
memoize-fs
东西,它将函数调用的结果缓存到文件系统,以节省我们一些网络/cpu 时间。 Could we open the file for reading if it exists, and for writing if it doesn't, all in the single call?如果文件存在,我们可以打开文件进行读取,如果不存在则进行写入,所有这些都可以在一次调用中完成吗? Let's take a funny look on those flags.
让我们来有趣的看看这些标志。 After a while of mental exercises we can see that
a+
does what we want: if the file doesn't exist, it creates one and opens it both for reading and writing, and if the file exists it does so without clearing the file (as w+
would).经过一段时间的心理练习,我们可以看到
a+
做了我们想要的:如果文件不存在,它会创建一个并打开它进行读写,如果文件存在,它不会清除文件(如w+
会)。 But now we cannot use it neither in (smth)File
, nor in create(Smth)Stream
functions.但是现在我们既不能在
(smth)File
使用它,也不能在create(Smth)Stream
函数中使用它。 And that seems like a missing feature.这似乎是一个缺失的功能。
So feel free to file it as a feature request (or even a bug) to Node.js github, as lack of atomic asynchronous file system API is a drawback of Node.因此,请随意将其作为功能请求(甚至是错误)提交给 Node.js github,因为缺乏原子异步文件系统 API 是 Node.js 的一个缺点。 Though don't expect changes any time soon.
虽然不要指望很快会有变化。
Edit.编辑。 I would like to link to articles by Linus and by Dan Luu on why exactly you don't want to do anything smart with your
fs
calls, because the claim was left mostly not based on anything.我想链接到Linus和Dan Luu 的文章,这些文章讨论了为什么您不想对
fs
调用做任何聪明的事情,因为该声明大多没有基于任何内容。
This method is no longer recommended.不再推荐此方法。 fs.exists is deprecated.
fs.exists 已弃用。 See comments.
看评论。
Here are some options:以下是一些选项:
1) Have 2 "fs" calls . 1) 有2 个“fs”调用。 The first one is the " fs.exists " call, and the second is " fs.write / read, etc"
第一个是“ 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) Or Create an empty file first: 2)或者先创建一个空文件:
--- Sync: - - 同步:
//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) Or use " touch ": https://github.com/isaacs/node-touch 3)或使用“触摸”: https : //github.com/isaacs/node-touch
Todo this in a single system call you can use the fs-extra
npm module.要在单个系统调用中完成此操作,您可以使用
fs-extra
npm 模块。 After this the file will have been created as well as the directory it is to be placed in.在此之后,将创建文件以及放置它的目录。
const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFile(file, err => {
console.log(err) // => null
});
Another way is to use ensureFileSync which will do the same thing but synchronous.另一种方法是使用ensureFileSync它将做同样的事情,但同步。
const fs = require('fs-extra');
const file = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureFileSync(file)
With async
/ await
and Typescript I would do:使用
async
/ await
和 Typescript 我会这样做:
import * as fs from 'fs'
async function upsertFile(name: string) {
try {
// try to read file
await fs.promises.readFile(name)
} catch (error) {
// create empty file, because it wasn't found
await fs.promises.writeFile(name, '')
}
}
Here's a synchronous way of doing it:这是一种同步的方式:
try {
await fs.truncateSync(filepath, 0);
} catch (err) {
await fs.writeFileSync(filepath, "", { flag: "wx" });
}
If the file exists it will get truncated, otherwise it gets created if an error is raised.如果文件存在,它将被截断,否则如果引发错误,它将被创建。
You can do something like this:你可以这样做:
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);
}
});
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.