1. Road King 処理モジュールのパス
1.1 モジュールの概要
Nodejs では path は頻繁に使用されるモジュールですが、好き嫌いが分かれます。理由の 1 つはドキュメントが十分に明確ではないこと、もう 1 つはインターフェイスのプラットフォームの違いによるものです。パスのインターフェースを目的に応じて分類し、よく考えればそれほど混乱することはありません。
1.2 パス/ファイル名/拡張子の取得
- パスを取得します: path.dirname(filepath)
- ファイル名を取得します: path.basename(filepath)
- 拡張子を取得します: path.extname(filepath)
(1) 配置されているパスを取得します。
例は次のとおりです。
var path = require('path');
var filepath = '/tmp/demo/js/test.js';
// 输出:/tmp/demo/js
console.log( path.dirname(filepath) );
(2) ファイル名の取得
path.basename(filepath) は厳密には出力パスの最後の部分であり、ファイル名かどうかは判断しません。ただし、ほとんどの場合は、単純な「ファイル名を取得する」メソッドとして使用できます。
var path = require('path');
// 输出:test.js
console.log( path.basename('/tmp/demo/js/test.js') );
// 输出:test
console.log( path.basename('/tmp/demo/js/test/') );
// 输出:test
console.log( path.basename('/tmp/demo/js/test') );
ファイル名だけを取得したいが、ファイル拡張子は取得したくない場合はどうすればよいでしょうか? 2 番目のパラメータを使用できます。
// 输出:test
console.log( path.basename('/tmp/demo/js/test.js', '.js') );
(3) ファイル拡張子を取得します。
簡単な例は次のとおりです。
var path = require('path');
var filepath = '/tmp/demo/js/test.js';
// 输出:.js
console.log( path.extname(filepath) );
より詳細なルールは次のとおりです: ( path.basename(filepath) === B と仮定します)
- B の最後の . から最後の文字までをインターセプトします。
- B に . が存在しない場合、または B の最初の文字が . である場合は、空の文字列を返します。
公式ドキュメントの例を直接見てみる
path.extname('index.html')
// returns '.html'
path.extname('index.coffee.md')
// returns '.md'
path.extname('index.')
// returns '.'
path.extname('index')
// returns ''
path.extname('.index')
// returns ''
1.3 パスの組み合わせ
path.join([...paths])
path.resolve([...paths])
(1) path.resolve()で生成される絶対パス
構文形式:
path.resolve([...myPaths])
説明:
- 一連のパスまたはパスのフラグメントを絶対パスとして解決します。
- 返されたパスは右から左に処理され、完全な絶対パスが構築されるまで後続の各 myPath が順番に解析されます。
你可以想象现在你在shell下面,从左到右运行一遍cd path命令,最终获取的绝对路径/文件名,就是这个接口所返回的结果了。
コード例:
const path = require('path');
let arr1 = ['/foo1/foo2', 'dselegent', 'foo3'];
let result1 = path.resolve(...arr1);
console.log(result1); // 打印结果:/foo1/foo2/dselegent/foo3
let arr2 = ['/foo1/foo2', '/dselegent', 'foo3'];
let result2 = path.resolve(...arr2);
console.log(result2); // 打印结果:/dselegent/foo3
const path = require('path');
// 假设当前工作路径是 /Users/a/Documents/git-code/nodejs-learning-guide/examples/2016.11.08-node-path
// 输出 /Users/a/Documents/git-code/nodejs-learning-guide/examples/2016.11.08-node-path
console.log( path.resolve('') )
// 输出 /Users/a/Documents/git-code/nodejs-learning-guide/examples/2016.11.08-node-path
console.log( path.resolve('.') )
// 输出 /foo/bar/baz
console.log( path.resolve('/foo/bar', './baz') );
// 输出 /foo/bar/baz
console.log( path.resolve('/foo/bar', './baz/') );
// 输出 /tmp/file
console.log( path.resolve('/foo/bar', '/tmp/file/') );
// 输出 /Users/a/Documents/git-code/nodejs-learning-guide/examples/2016.11.08-node-path/www/js/mod.js
console.log( path.resolve('www', 'js/upload', '../mod.js') );
(2) path.join() は複数のパスを結合します
手動でパスを結合すると、間違いが起こりやすくなります。このとき、 path.join() メソッドを使用してパスを結合できます。
文法形式:
path.join([...paths]);
説明: プラットフォーム固有の区切り文字を区切り文字として使用して、指定されたすべてのパスフラグメントを連結し、結果のパスを正規化します。
コード例:
const path = require('path');
const result1 = path.join(__dirname, './app.js');
console.log(result1); // 返回:/Users/smyhvae/dselegent/app.js
const result2 = path.join('/foo1', 'foo2', './foo3');
console.log(result2); // 返回:/foo1/foo2/foo3
const result3 = path.join('/foo1', 'foo2', '/foo3');
console.log(result3); // 返回:/foo1/foo2/foo3
(3)path.resolve
と のpath.join
path.resolve
違いはpath.join
、両方ともパス コア モジュールの下でパスを接続するために使用されるメソッドです。
完全なパスに連結できます。
const path = require("path");
var dirname = '/User/Desktop';
var basename = 'abc.txt';
path.join(dirname, basename); // /User/Desktop/abc.txt
path.resolve(dirname, basename); // /User/Desktop/abc.txt
dirname が ./、.../ で始まり、/ がない場合、resolve はディスクの下のルート ディレクトリを検索します。
const path = require("path");
var dirname = '../User/Desktop';
var basename = 'abc.txt';
path.join(dirname, basename); // ../User/Desktop/abc.txt
path.resolve(dirname, basename); // /Users/Desktop/node/User/Desktop/abc.txt
ベース名が / で始まる場合、resolve はベース名を直接返します。
const path = require("path");
var dirname = '/User/Desktop';
var basename = '/abc.txt';
path.join(dirname, basename); // /User/Desktop/abc.txt
path.resolve(dirname, basename); // /abc.txt
1.4 いくつかの共通パス
__dirname
: これは定数であり、現在の実行ファイルが配置されている完全なディレクトリを意味します。
__filename
:これは定数です。示す内容: 完全なディレクトリ + 現在実行されているファイルのファイル名。
process.cwd: Node コマンドが現在実行されているときのディレクトリ名を取得します。
コード例:
console.log(__dirname);
console.log(__filename);
console.log(process.cwd());
操作結果:
$ node app.js
/Users/smyhvae/dselegent
/Users/smyhvae/dselegent/app.js
/Users/smyhvae/dselegent
2. ローカルファイル操作モジュール fs
Node.js における同期と非同期の違い
fs モジュールには、ファイルに対するほぼすべての操作に対して同期と非同期の 2 つの形式があります。例:readFile()
とreadFileSync()
。
違い:
- 同期呼び出しはコードの実行をブロックしますが、非同期呼び出しはブロックしません。
- 非同期呼び出しは読み取りタスクをタスク キューに送信し、タスクの実行が完了するまでコールバックしません。
- 例外処理: 同期では try catch メソッドを使用する必要があります。非同期ではコールバック関数の最初のパラメーターを渡すことができます。【
★★★★★
】
2.1 ファイルの読み込み
同期読み取り
var fs = require('fs');
var data;
try{
data = fs.readFileSync('./fileForRead.txt', 'utf8');
console.log('文件内容: ' + data);
}catch(err){
console.error('读取文件出错: ' + err.message);
}
出力は次のとおりです。
/usr/local/bin/node readFileSync.js
文件内容: hello world
非同期読み取り
var fs = require('fs');
fs.readFile('./fileForRead.txt', 'utf8', function(err, data){
if(err){
return console.error('读取文件出错: ' + err.message);
}
console.log('文件内容: ' + data);
});
出力は次のとおりです
/usr/local/bin/node readFile.js
文件内容: hello world
fs/promises 从 Node.js 14 开始可用 从 Node.js 14 开始,fs 模块提供了两种使用基于 promises 的文件系统的方法。这些 promises 可以通过 require('fs').promises 或 require('fs/promises') 获得。
import {
readFile } from 'fs/promises';
try {
const contents = await readFile(filePath, {
encoding: 'utf8' });
console.log(contents);
} catch (err) {
console.error(err.message);
}
2.2 ファイルの書き込み
備考: 次のコードは、ファイルが存在しない場合はファイルを作成し、ファイルが存在する場合はファイルの内容を上書きします。
非同期で書き込む
var fs = require('fs');
fs.writeFile('./fileForWrite.txt', 'hello world', 'utf8', function(err){
if(err) throw err;
console.log('文件写入成功');
});
同期的に書き込む
var fs = require('fs');
try{
fs.writeFileSync('./fileForWrite1.txt', 'hello world', 'utf8');
console.log('文件写入成功');
}catch(err){
throw err;
}
約束
import {
writeFile } from 'fs/promises';
try {
const contents = await writeFile('message.txt', 'hello world', {
encoding: 'utf8' });
console.log(contents);
} catch (err) {
// When a request is aborted - err is an AbortError
console.error(err);
}
2.3 ファイルが存在するかどうか
fs.exists()
すでにその状態になっているdeprecated
ので、以下のコードでファイルが存在するかどうかを判断できるようになります。
非同期バージョン
const fs = require('fs')
//检查文件是否存在于当前目录中
fs.access('package.json', fs.constants.F_OK, err => {
if(err) {
console.log('package.json不存在于当前目录中')
return
}
console.log('package.json存在于当前目录中')
})
fs.access('index.js', fs.constants.F_OK, err => {
if(err) {
console.log('index.js不存在于当前目录中')
return
}
console.log('index.js存在于当前目录中')
})
fs.access()
ファイルが存在するかどうかの判定(デフォルトモード)に加えて、ファイルのパーミッションの判定にも使用できます。
備考:fs.constants.F_OK
定数を取得できません (ノード v6.1、Mac 10.11.4 では、fs.constants
はいundefined
)
同期する
import {
accessSync, constants } from 'fs';
try {
accessSync('etc/passwd', constants.R_OK );
console.log('can read');
} catch (err) {
console.error('no access!');
}
約束
import {
access, constants } from 'node:fs/promises';
try {
await access('/etc/passwd', constants.R_OK);
console.log('can access');
} catch {
console.error('cannot access');
}
2.4 ファイルの削除
非同期バージョン
var fs = require('fs');
fs.unlink('./fileForUnlink.txt', function(err){
if(err) throw err;
console.log('文件删除成功');
});
同期バージョン
import {
unlinkSync } from 'fs';
try {
unlinkSync('/tmp/hello');
console.log('successfully deleted /tmp/hello');
} catch (err) {
// handle the error
}
約束
import { unlink } from 'fs/promises';
try {
await unlink('/tmp/hello');
console.log('successfully deleted /tmp/hello');
} catch (err) {
// handle the error
}
2.5 ディレクトリの作成
非同期バージョン(ディレクトリがすでに存在する場合はエラーがスローされます)
// fs.mkdir(path[, mode], callback)
var fs = require('fs');
fs.mkdir('sub', function(err){
if(err) throw err;
console.log('创建目录成功');
});
同期バージョン
// fs.mkdirSync(path[, mode])
var fs = require('fs');
try{
fs.mkdirSync('hello');
console.log('创建目录成功');
}catch(e){
throw e;
}
約束
import {
mkdir } from 'fs/promises';
try {
const createDir = await mkdir(projectFolder, {
recursive: true });
console.log(`created ${
createDir}`);
} catch (err) {
console.error(err.message);
}
2.6 ディレクトリの移動
同期バージョン、注意: fs.readdirSync() は 1 つのレイヤーのみを読み取るため、ファイル タイプがディレクトリであるかどうかを判断し、ディレクトリである場合は再帰的トラバーサルを実行する必要があります。
// fs.readdirSync(path[, options])
var fs = require('fs');
var path = require('path');
var getFilesInDir = function(dir){
var results = [ path.resolve(dir) ];
var files = fs.readdirSync(dir, 'utf8');
files.forEach(function(file){
file = path.resolve(dir, file);
var stats = fs.statSync(file);
if(stats.isFile()){
results.push(file);
}else if(stats.isDirectory()){
results = results.concat( getFilesInDir(file) );
}
});
return results;
};
var files = getFilesInDir('../');
console.log(files);
2.7 ディレクトリの読み取り
import {
readdir } from 'fs/promises';
try {
const files = await readdir(path);
for (const file of files)
console.log(file);
} catch (err) {
console.error(err);
}
2.8 ディレクトリの削除
// 删除目录(前提没有文件在里面)
fs.rmdir('./avatar', err => {
if (err && err.code === 'ENOENT') {
console.log('目录不存在');
}
});
2.9 ディレクトリ全体を削除する
//1
const fs = require("fs")
fs.("./avatar",(err,data)=>{
// console.log(data)
data.forEach(item=>{
fs.unlinkSync(`./avatar/${
item}`)
})
fs.rmdir("./avatar",(err)=>{
console.log(err)
})
})
//2
const fs = require('fs')
fs.readdir("./avatar").then(async (data)=>{
let arr = []
data.forEach(item=>{
arr.push(fs.unlink(`./avatar/${
item}`))
})
await Promise.all(arr)
fs.rmdir("./avatar")
})
//3
const fs = require('fs').promises;
fs.readdir('./image2').then(async data => {
await Promise.all(data.map(item => fs.unlink(`./image2/${
item}`)));
await fs.rmdir('./image2');
});
2.10 ファイル名の変更
非同期バージョン
// fs.rename(oldPath, newPath, callback)
var fs = require('fs');
fs.rename('./hello', './world', function(err){
if(err) throw err;
console.log('重命名成功');
});
同期バージョン
// fs.renameSync(oldPath, newPath)
var fs = require('fs');
fs.renameSync('./world', './hello');
promises
import {
rename } from 'fs/promises';
try {
await rename('./world', './hello');
console.log(`rename`);
} catch (err) {
console.error(err.message);
}
2.11 ファイルステータスの取得
(1) 非同期: fs.stat(path, callback): path はパスを表す文字列で、コールバックは 2 つのパラメータ (err、stats) を受け取ります。ここで、stats は fs.stats のインスタンスです。
(2) 同期: fs.statSync(path) はパス変数を受け取るだけであり、fs.statSync(path) は実際には fs.stats のインスタンスです。
方法
- stats.isFile() – ファイルかどうか
- stats.isDirectory() – ディレクトリかどうか
// Node.js program to demonstrate the
// fs.statSync() method
// Import the filesystem module
const fs = require('fs');
// Getting information for a file
statsObj = fs.statSync("test_file.txt");
console.log(statsObj);
console.log("Path is file:", statsObj.isFile());
console.log("Path is directory:", statsObj.isDirectory());
// Getting information for a directory
statsObj = fs.statSync("test_directory");
console.log(statsObj);
console.log("Path is file:", statsObj.isFile());
console.log("Path is directory:", statsObj.isDirectory());
出力:
Stats {
dev:3229478529,
mode:33206,
nlink:1,
uid:0,
gid:0,
rdev:0,
blksize:4096,
ino:1970324837039946,
size:0,
blocks:0,
atimeMs:1582306776282,
mtimeMs:1582482953967,
ctimeMs:1582482953968.2532,
birthtimeMs:1582306776282.142,
atime:2020-02-21T17:39:36.282Z,
mtime:2020-02-23T18:35:53.967Z,
ctime:2020-02-23T18:35:53.968Z,
birthtime:2020-02-21T17:39:36.282Z
}
Path is file:true
Path is directory:false
Stats {
dev:3229478529,
mode:16822,
nlink:1,
uid:0,
gid:0,
rdev:0,
blksize:4096,
ino:562949953486669,
size:0,
blocks:0,
atimeMs:1582482965037.8445,
mtimeMs:1581074249467.7114,
ctimeMs:1582482964979.8303,
birthtimeMs:1582306776288.1958,
atime:2020-02-23T18:36:05.038Z,
mtime:2020-02-07T11:17:29.468Z,
ctime:2020-02-23T18:36:04.980Z,
birthtime:2020-02-21T17:39:36.288Z
}
Path is file:false
Path is directory:true
2.12 ファイルの内容を追加する
fs.appendFile(file, data[, options], callback)
- file: ファイル パスまたはファイル ハンドルを指定できます。(緩衝材にもなるのかな?)
- data: 追加するコンテンツ。文字列またはバッファ。
- オプション
- エンコーディング: エンコーディング、デフォルトは utf8 です
- モード: デフォルトは 0o666
- フラグ: デフォルトは
注: file がファイル ハンドルの場合、
- データの追加を開始する前に、ファイルを開く必要があります。
- ファイルを手動で閉じる必要があります。
var fs = require('fs');
fs.appendFile('./extra/fileForAppend.txt', 'hello', 'utf8', function(err){
if(err) throw err;
console.log('append成功');
});
3. イベントメカニズムモジュールのイベント
Node.js には複数の組み込みイベントがあり、次の例に示すように、events
モジュールを導入し、クラスをインスタンス化することで、EventEmitter
イベントをバインドしてリッスンできます。
// 引入 events 模块
var EventEmitter = require('events');
// 创建 eventEmitter 对象
var event = new EventEmitter();
次のプログラムはイベント ハンドラーをバインドします。
// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);
プログラムでイベントをトリガーできます。
// 触发事件
eventEmitter.emit('eventName');
EventEmitter
の各イベントは、イベント名といくつかのパラメータで構成されます。イベント名は通常、特定のセマンティクスを表す文字列です。イベントごとに、EventEmitter
複数のイベント リスナーがサポートされます。
イベントがトリガーされると、このイベントに登録されているイベント リスナーが順番に呼び出され、イベント パラメータがコールバック関数のパラメータとして渡されます。
次の例を使用してこのプロセスを説明しましょう。
// 引入 events 模块
var EventEmitter = require('events');
// 创建 eventEmitter 对象
var event = new EventEmitter();
event.on('someEvent', function(arg1, arg2) {
console.log('listener1', arg1, arg2);
});
event.on('someEvent', function(arg1, arg2) {
console.log('listener2', arg1, arg2);
});
event.emit('someEvent', 'arg1 参数', 'arg2 参数');
上記のコードを実行すると、操作の結果は次のようになります。
$ node event.js
listener1 arg1 参数 arg2 参数
listener2 arg1 参数 arg2 参数
上記の例では、 2 つのイベント リスナーがevent
イベントにsomeEvent
登録され、someEvent
イベントがトリガーされます。
実行結果では、2 つのイベント リスナー コールバック関数が連続して呼び出されていることがわかります。これがEventEmitter
最も簡単な使い方です。
EventEmitter
on
やなど、いくつかのプロパティが提供されていますemit
。on
関数はイベント関数をバインドするために使用され、emit
プロパティはイベントをトリガーするために使用されます。