节奏大师 發表於 2022-6-29 07:58:00

Node.js精进(6)——文件

<p>  文件系统是一种用于向用户提供底层数据访问的机制,同时也是一套实现了数据的存储、分级组织、访问和获取等操作的抽象数据类型。</p>
<p>  Node.js 中的<span style="color: rgba(51, 102, 255, 1)"><span style="color: rgba(51, 102, 255, 1)">fs模块</span></span>就是对文件系统的封装,整合了一套标准 POSIX 文件 I/O 操作的集合,包括文件的读写、删除、遍历、重命名等操作。</p>
<p>  fs 模块中的所有方法都提供了三种形式:回调、同步和 Promise ,其中 Promise 是在 Node.js 的版本 10 中引入的。</p>
<p>  本系列所有的示例源码都已上传至Github,<span style="color: rgba(51, 102, 255, 1)"><span style="color: rgba(51, 102, 255, 1)">点击此处</span></span>获取。&nbsp;</p>
<h1>一、三种形式</h1>
<p>  在回调形式的方法中,最后一个参数是其回调函数,会异步地调用,其中回调函数的第一个参数始终为异常预留,不过有个例外是 exists() 方法。</p>
<p>  回调形式不容易书写,很容易就会形成回调地狱。</p>
<p>  虽然同步形式的方法比较容易书写,但是在执行时会阻止 Node.js 事件循环和阻塞 JavaScript 执行,直到操作完成。</p>
<p>  Promise 形式的方法会使用底层的 Node.js 线程池,在事件循环线程之外异步地执行文件系统操作。对同一文件执行多个并发修改时必须小心,有可能会损坏数据。</p>
<p>  以读取文件为例,三种形式的写法如下所示,若不指定编码,那么输出的将是 Buffer 实例。</p>
<div class="cnblogs_code">
<pre>const fs = require('fs'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 回调</span>
fs.readFile('./data.txt', 'utf8', (err, data) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (err) <span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)"> err;
console.log(data);    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> strick</span>
<span style="color: rgba(0, 0, 0, 1)">});

</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 同步</span>
const data = fs.readFileSync('./data.txt', 'utf8'<span style="color: rgba(0, 0, 0, 1)">);
console.log(data);    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> strick</span>

<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Promise</span>
const { promises } =<span style="color: rgba(0, 0, 0, 1)"> fs;
async </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> readFilePromise() {
const data </span>= await promises.readFile('./data.txt', 'utf8'<span style="color: rgba(0, 0, 0, 1)">);
console.log(data);    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> strick</span>
<span style="color: rgba(0, 0, 0, 1)">}
readFilePromise();</span></pre>
</div>
<h1>二、基础使用</h1>
<p><span style="font-size: 16px"><strong>1)判断文件是否存在</strong></span></p>
<p>  exists() 方法可用于判断文件是否存在,但在上一小节中曾提到,它已被弃用。</p>
<p>  这是因为此回调的参数与其他回调不一致。通常,Node.js 回调的第一个参数是 err 参数,然后跟可选的其他参数,但 fs.exists() 回调只有一个布尔参数,如下所示。</p>
<div class="cnblogs_code">
<pre>fs.exists('./data.txt', isExist =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
console.log(isExist);
});</span></pre>
</div>
<p>  再则是因为 exists() 方法的功能用 access() 方法也能实现,其内部源码如下所示,其实也是调用了 access() 方法。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> exists(path, callback) {
maybeCallback(callback);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构造回调函数</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> suppressedCallback(err) {
    callback(err </span>? <span style="color: rgba(0, 0, 255, 1)">false</span> : <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
    fs.access(path, F_OK, suppressedCallback);
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> callback(<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<p>  其中 F_OK 是 fs 模块中的一个常量,表示文件是否存在,使用方法如下所示,R_OK 表示是否可读,W_OK 表示是否可写。</p>
<div class="cnblogs_code">
<pre>const { constants } = require('fs'<span style="color: rgba(0, 0, 0, 1)">);
const {F_OK,R_OK,W_OK } </span>= constants;</pre>
</div>
<p>  注意,在调用 fs.open()、fs.readFile() 或 fs.writeFile() 之前,不能使用 fs.access() 检查文件是否存在。</p>
<p>  因为这样做会引入竞争条件,其他进程可能会在两次调用之间修改文件状态,造成非预期的结果。</p>
<p>  遇到这种场景,推荐的做法是直接打开、读取或写入文件,当文件不可用时再做处理。</p>
<p>  另一种判断文件是否存在的方法是调用 stat(),读取文件属性。</p>
<p>  它有两个方法 isDirectory() 和 isFile() 可分别判断是否是目录和是否是文件,如下所示。</p>
<div class="cnblogs_code">
<pre>fs.stat('./data.txt', (err, stats) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
console.log(stats.isDirectory());
console.log(stats.isFile());
});</span></pre>
</div>
<p>  同样要注意的是,它也不能在调用 fs.open()、fs.readFile() 或 fs.writeFile() 之前,检查文件是否存在。</p>
<p><span style="font-size: 16px"><strong>2)方法</strong></span></p>
<p>  下面罗列的是 fs 模块的一些方法。</p>
<ul>
<li>fs.open():打开文件,可设置文件模式。</li>
<li>fs.close():关闭文件描述符。</li>
<li>fs.createReadStream():创建可读的文件流。</li>
<li>fs.createWriteStream():创建可写的文件流。</li>
<li>fs.readFile():读取文件的内容,相关方法:fs.read()。</li>
<li>fs.writeFile():写入文件,相关方法:fs.write()。</li>
<li>fs.link():新建指向文件的硬链接。</li>
<li>fs.unlink():删除文件或符号链接。</li>
<li>fs.mkdir():新建文件夹。</li>
<li>fs.rmdir():删除文件夹。</li>
<li>fs.readdir():读取目录的内容。</li>
<li>fs.stat():读取文件属性,相关方法:fs.fstat()、fs.lstat()。</li>
<li>fs.access():检查文件是否存在,以及 Node.js 是否有权限访问。</li>
<li>fs.rename():重命名文件或文件夹。</li>
<li>fs.appendFile():追加数据到文件,如果文件不存在,则创建文件。</li>
<li>fs.copyFile():拷贝文件,可覆盖文件内容。</li>
<li>fs.chmod():更改文件(通过传入的文件名指定)的权限,相关方法:fs.lchmod()、fs.fchmod()。</li>
<li>fs.chown():更改文件(通过传入的文件名指定)的所有者和群组,相关方法:fs.fchown()、fs.lchown()。</li>
<li>fs.watchFile():开始监控文件的更改,相关方法:fs.watch()。</li>
<li>fs.unwatchFile():停止监控文件的更改。</li>
</ul>
<p><span style="font-size: 16px"><strong>3)路径</strong></span></p>
<p>  路径处理并不是在 fs 模块,而是在<span style="color: rgba(51, 102, 255, 1)"><span style="color: rgba(51, 102, 255, 1)">path模块</span></span>,它的方法包括。</p>
<ul>
<li>path.basename():读取路径的最后一部分。</li>
<li>path.dirname():读取路径的目录部分。</li>
<li>path.extname():读取路径的文件扩展名。</li>
<li>path.isAbsolute():判断是否是绝对路径。</li>
<li>path.join():将多个部分合并成一个完整的路径。</li>
<li>path.normalize():当包含类似 .、.. 或 // 等相对的说明符时,就尝试计算实际的路径。</li>
<li>path.parse():解析成路径对象。</li>
<li>path.relative():基于当前目录,返回从第一个路径到第二个路径的相对路径。</li>
<li>path.resolve():将相对路径计算成绝对路径。</li>
</ul>
<div class="cnblogs_code">
<pre>path.basename('../06/data.txt')    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> data.txt</span>
path.dirname('../06/data.txt');    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ../06</span>
path.extname('../06/data.txt');    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> .txt</span>
path.isAbsolute('../06/data.txt');   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> false</span>
path.join('../', '06', 'data.txt');    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ../06/data.txt</span>
path.normalize('/../06/data.txt');   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> /06/data.txt</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> { root: '', dir: '../06', base: 'data.txt', ext: '.txt', name: 'data' }</span>
path.parse('../06/data.txt'<span style="color: rgba(0, 0, 0, 1)">);
path.relative(</span>'../', '../06/data.txt');    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 06/data.txt</span>
path.resolve('../06/data.txt');      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> /Users/code/web/node/06/data.txt</span></pre>
</div>
<p>&nbsp;</p>
<p>参考资料:</p>
<p>判断文件存在<br></p>
<p>深入Node.js源码之文件系统</p>
<p>Node.js官网文档&nbsp;API文件系统</p>
<p>饿了么File</p><br><br>
来源:https://www.cnblogs.com/strick/p/16252310.html
頁: [1]
查看完整版本: Node.js精进(6)——文件