Javascriptは現在Webアプリケーションだけではなく、デスクトップアプリケーションの実装にも使われるようになり、他の言語と遜色のない言語になりました。
今回、開発環境の構築で少しJavascriptを使う機会があったのでサーバーサイドのJavascriptについて少しご紹介したいと思います。
サーバーサイドと言いましたが、Webサーバーだけで動くものと言う訳ではなく、自身のマシンに実行環境をセットアップしてコマンドラインからプログラムを実行できます。
ブラウザ上で実行するJavascriptは、ブラウザを超えてOSのファイルを操作したりすることはセキュリティ上不可能となっています。けれど、サーバーサイドで実行するJavascriptはファイルを更新したり削除したりする操作やOSの機能を利用することが可能になります。
まずJavascriptをサーバーサイドで実行するにはNode.jsが必要です。
「Node.js インストール」などでググると役立つページがたくさんがありますので、インストール方法は今回は割愛させていただきます。
プログラムは基本的にソースコードの上から下へ1行ずつ順番に処理をしていきます。
この処理というのは同期的で、1つの処理が完了するのを待ってから次の行の処理へ進むようになっています。
もしある1つの処理がとても時間のかかるものだった場合、その処理が完了するまで次の行の処理へプログラムは進むことができません。
非同期処理とは、1つ処理の完了を待たずに、時間のかかる処理を追い越して次の行の処理へ進みます。
追い越された処理は実行できる準備が整ってから実行されます。
例えば、サーバーへ「アカウントの情報が欲しい」といったリクエストを送ったらサーバーからレスポンスが返ってくるまで通信に時間がかかります。
この間、レスポンスが返ってくるまで以降の処理を止めるのではなく、リクエストを送った処理はレスポンスが返ってくるまで待機し、以降の処理をどんどん進めます。
この時、待機中のリクエスト処理にはコールバック関数と呼ばれる、処理が実行できる時点で呼び出される関数をあらかじめ引数に渡し、レスポンスが返ってきた時点で現在の処理に割り込んで実行することができます。
非同期処理は時間のかかる処理を飛ばすことができるので、プログラムの処理は同期処理に比べ早くなりますが、全ての処理を非同期で行うと返って都合が悪い時もあります。
そのためにJavascriptは同じ処理に対して、同期的に処理を行う関数と非同期で処理を行う関数をそれぞれ用意しています。
同期して処理を行う関数には、共通して関数名の末尾に[Sync]が付きます。
Node.jsでファイルを扱うにはfsモジュールを使います。fsは「File System」の略です。
fsモジュールはファイルやディレクトリを操作するためのAPIを用意してくれています。
Node.jsの公式モジュールであり、Node.jsと一緒にマシンにインストールされます。
今回はこのfsモジュールを使ってファイルの基本操作となる新規作成、読み込み、上書き、削除などJavascriptで実行する方法を紹介します。
モジュールを利用するには以下をソースコードの先頭に記述します。
1 |
const fs = require('fs'); |
ファイルの読み込みはfs.readFileSyncとfs.readFileの2種類の関数があります。
先に説明したようにreadFileSyncは同期的処理を行う関数で、readFileは非同期処理を行う関数です。
1 2 3 4 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの読み込み let data = fs.readFileSync(filepath,'utf8'); |
第1引数にはファイルパスを、第2引数はファイルの文字エンコードを指定します。
返り値にファイルの中身が返されます。
1 2 3 4 5 6 7 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの読み込み fs.readFile(filepath,function(err, file) { //読み込み完了後の処理を書く if (err) throw err //エラーが返ってきた場合はエラーを投げる }); |
第1引数にはファイルパスを、第2引数はコールバック関数を渡します。
コールバック関数に設定されている引数についてですが、[err]はファイルの読み込みに失敗した場合のエラーが渡され、[file]は読み込んだファイルの中身が渡されます。
非同期処理では、ファイル取得後の処理はこのコールバック関数の中に書いていくことになります。
ファイルの上書き、新規作成はfs.writeFileSyncとfs.writeFileの2種類の関数があります。
1 2 3 4 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの書き込み fs.writeFileSync(filepath,data); |
第1引数にはファイルパスを、第2引数はファイルの中身を指定します。
戻り値は[undefind]です。
1 2 3 4 5 6 7 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの書き込み fs.writeFile(filepath,data, function(err) { //書き込み完了後の処理をかく if (err) throw err //エラーが返ってきた場合はエラーを投げる }); |
第1引数にはファイルパスを、第2引数はファイルの中身を指定します。
第3引数はコールバック関数となり、書き込み完了後の処理を関数で渡します。
ファイルの削除はfs.unlinkSyncとfs.unlinkの2種類の関数があります。
1 2 3 4 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの削除 fs.unlinkSync(filepath); |
第1引数にファイルパスを渡します。
1 2 3 4 5 6 7 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの削除 fs.unlink(filepath, function(err) { //削除後の処理をかく if (err) throw err //エラーが返ってきた場合はエラーを投げる }); |
第1引数にファイルパスを指定します。
第2引数はコールバック関数となり、書き込み完了後の処理を関数で渡します。
ファイルが存在するかしないかをチェックする関数です。fs.statSyncとfs.statの2種類の関数があります。
1 2 3 4 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの確認 fs.statSync(filepath); |
第1引数にファイルパスを渡します。戻り値はファイルオブジェクトになります。
1 2 3 4 5 6 7 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの確認 fs.stat(filepath, function(err, stat) { //コールバック処理をかく if (err) throw err //エラーが返ってきた場合はエラーを投げる }); |
第1引数にファイルパスを指定します。第2引数はコールバック関数です。
最後にディレクトリの削除です。
ディレクトリの削除はfs.rmdirSyncとfs.rmdirの2種類の関数があります。
1 2 3 4 |
const fs = require('fs'); // fsモジュール読み込み // ディレクトリの削除 fs.rmdirSync(filepath); |
引数にファイルパスを渡します。
1 2 3 4 5 6 7 |
const fs = require('fs'); // fsモジュール読み込み // ファイルの確認 fs.rmdir(filepath, function(err) { //削除後の処理をかく if (err) throw err //エラーが返ってきた場合はエラーを投げる }); |
第1引数にファイルパスを指定します。第2引数はコールバック関数です。
fsモジュールのrmdir関数は再帰的にディレクトリを削除できません。rimrafモジュールやfs-extraモジュールを利用して再帰的に削除することができますが、今回は再帰関数を作ってfsモジュールの関数でディレクトリを再帰的に削除します。
ソースコードは以下になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const fs = require('fs'); // fsモジュール読み込み // ディレクトリを再帰的に削除する関数 function removeDirectory(dirpath, callback) { if (fs.statSync(dirpath).isDirectory()) { let removeFiles = fs.readdirSync(dirpath); for (var file in removeFiles) { let filepath = dirpath + '/' + removeFiles[file]; if (fs.statSync(filepath).isDirectory()) { removeDirectory(filepath, function (removeDirpath) { fs.rmdirSync(removeDirpath); console.log(`ディレクトリ[${removeDirpath}]を削除しました。`); }); } else { fs.unlinkSync(filepath); console.log(`ファイル[${filepath}]を削除しました。`); } } if (callback) callback(dirpath); //コールバックでディレクトリを削除 } else { console.error(`[${dirpath}]はディレクトリではありません。`); } } // "コンテンツ"ディレクトリを中身ごと削除 removeDirectory("contents"); |
ソースコードの中身を見ていきましょう。
コードのほとんどはremoveDirectory関数の実装になります。
1 |
const fs = require('fs'); // fsモジュール読み込み |
1 2 3 4 5 6 |
if(fs.statSync(dirpath).isDirectory()) { let removeFiles = fs.readdirSync(dirpath); 〜〜処理が続きます~~ } else { console.error(`[${dirpath}]はディレクトリではありません。`); } |
まずは引数で渡されたパスが本当にディレクトリなのかチェックします。
fs.statSync関数はファイルの存在チェックの関数で紹介しましたが、[.isDirectory()]のメソッドをでディレクトリかどうかをチェックすることもできます。
ディレクトリであった場合、続いてremoveFiles変数にディレクトリの中身を渡します。fs.readdirSync関数は引数で渡したディクトリの中身を返す関数です。
また、ディレクトリでない場合はコンソールにメッセージを出力します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
for (var file in removeFiles) { let filepath = dirpath + '/' + removeFiles[file]; if (fs.statSync(filepath).isDirectory()) { // fileがディレクトリ removeDirectory(filepath, function (removeDirpath) { fs.rmdirSync(removeDirpath); console.log(`ディレクトリ[${removeDirpath}]を削除しました。`); }); } else { // fileがファイル fs.unlinkSync(filepath); console.log(`ファイル[${filepath}]を削除しました。`); } } } if (callback) callback(dirpath); //コールバックでディレクトリを削除 |
forのループ処理でremoveFilesで取得したディレクトリの中身を1つずつ処理していきます。
ファイルだった場合は削除し、ディレクトリだった場合はremoveDirectory関数を呼び出してさらに中のファイルを再帰的に削除していきます。
1 2 3 4 |
removeDirectory(filepath, function (removeDirpath) { fs.rmdirSync(removeDirpath); console.log(`ディレクトリ[${removeDirpath}]を削除しました。`); }); |
removeDirectory関数の引数に渡しているコールバックはファイルの削除後にディレクトリ自体の削除を実行する処理になります。
1 2 |
// "コンテンツ"ディレクトリを中身ごと削除 removeDirectory("contents"); |
最後の行はremoveDirectory関数を最初に呼び出している部分です。
いかがでしたでしょうか。
僕自身、フロントエンドのみの担当が多く、ブラウザでのJavascriptの動作は熟知していてもサーバーサイドで利用される側面は知らない部分が多くなりがちです。
こういった知識を増やしていって普段の業務や制作環境の改善につなげていきたいですね。