情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

expressモジュールによるHTTPサーバ(14)フォルダの公開

この記事は「expressモジュールによるHTTPサーバ」という連載記事の14つ目の記事です。

その他の記事に関しては下を参照してください。

また、HTTP自体について知りたい方は下を参照してください。

この記事ではexpressモジュールを使ってフォルダを公開するHTTPサーバを作成する方法について説明します。

フォルダの公開

フォルダを公開するにはexpress.static関数を使用します。

var express = require('express');

var app = express();
app.use(express.static('xxx', {
    dotfiles: 'ignore', 
    etag: true, 
    extensions: false, 
    fallthrough: true, 
    index: 'index.html', 
    lastModified: true, 
    maxAge: 0, 
    redirect: true, 
    setHeaders: function (res, path, stat) {
    }
}));

第1引数にフォルダのパスを指定します。

第2引数にオプションをオブジェクトとして指定します。この引数は指定しなくても構いません。

主なオプションには下のようなものがあります。

  • index・・・フォルダのインデックスファイルの名称を指定します。falseを指定した場合にはインデックスファイルは存在しなかったものと見做します。デフォルトはindex.htmlです。
  • redirect・・・URLのパス部分が/で終わらないフォルダのパスである場合に/で終わるパスにリダイレクトするかを真偽値として指定します。デフォルトはtrueです。
  • dotfiles・・・名称が.から始まるファイルやフォルダを公開するかを指定します。allowを指定した場合には公開し、denyを指定した場合にはファイルやフォルダへのアクセスが拒否されたものと見做し、ignoreを指定した場合にはファイルやフォルダが存在しなかったものと見做します。デフォルトはignoreです。
  • fallthrough・・・処理においてエラーが発生した場合に次のミドルウェアの処理に移るか次のエラー処理ミドルウェアの処理に移るかを真偽値として指定します。trueの場合には次のミドルウェアの処理に移ります。falseの場合には次のエラー処理ミドルウェアの処理に移ります。デフォルトはtrueです。
  • extensions・・・0個以上の代替の拡張子を配列として指定します。
  • lastModified・・・HTTPレスポンスのLast-Modifiedヘッダフィールドの値をファイルの更新日時に設定するかを真偽値として指定します。デフォルトはtrueです。
  • maxAge・・・HTTPレスポンスのCache-Controlヘッダフィールドのmax-ageプロパティをミリ秒単位で指定します。デフォルトは0です。
  • etag・・・HTTPレスポンスのETagヘッダフィールドの値に弱いETagを設定するかを真偽値として指定します。デフォルトはtrueです。
  • setHeaders・・・HTTPレスポンスヘッダを設定する関数を指定します。この関数の第1引数はresであり、第2引数はファイルのパスであり、第3引数はファイルに対応するfs.Statsクラスのインスタンスです。

返り値としてミドルウェアが得られます。

サンプルコード1

クライアントからGETリクエストを受け取ったら現在のフォルダの対応するファイルのデータを返すHTTPサーバを作成し、起動します。

Enterキーが押されたらサーバを停止し、プログラムを終了します。

また、3番目のコマンドライン引数をexpress.static関数の第2引数のdotfilesオプションに指定します。

express-server-folder.js

var express = require('express');
var http = require('http');

if (process.argv.length < 3) {
    console.error('lack argument.');
    process.exit(1);
}
if (!['allow', 'deny', 'ignore'].some(function (elem, i, array) {
    return elem === process.argv[2];
})) {
    console.error('invalid argument.');
    process.exit(1);
}

var app = express();
app.set('env', 'development');
app.set('x-powered-by', false);
app.set('case sensitive routing', true);
app.set('strict routing', true);
app.use(express.static('.', {
    dotfiles: process.argv[2], 
    fallthrough: false, 
    index: 'index.txt'
}));
app.get('*', function (req, res) {
    res.status(404);
    res.send(http.STATUS_CODES[404] + '\r\n');
});
app.all('*', function (req, res) {
    res.status(501);
    res.send(http.STATUS_CODES[501] + '\r\n');
});
var server = app.listen(3000, function () {
    console.log('http server is running...press enter key to exit.');

    process.stdin.on('data', function (data) {
        if (data.indexOf('\n') !== -1) {
            server.close(function () {
                console.log('http server closed.');
                process.exit(0);
            });
        }
    });
});
server.on('error', function (err) {
    console.error(err);
    process.exit(1);
});

使用パッケージ

  • express
    npm install expressでインストールします。

実行結果

実行結果の確認のためにcURLを使用しています。

現在のフォルダには.textfile.txtという名称のテキストファイルが存在しています(文字コードはUTF-8です)。

C:\work\node>type .textfile.txt
これはテキストファイルの内容です。

また、現在のフォルダにはindex.txtという名称のテキストファイルも存在しています(文字コードはUTF-8です)。

C:\work\node>type index.txt
index

しかし、現在のフォルダにはindex2.txtという名称のテキストファイルは存在していません。

C:\work\node>type index2.txt
The system cannot find the file specified.

3つ目のコマンドライン引数にallowを指定してコードを実行するとHTTPサーバが起動します。

C:\work\node>node express-server-folder.js allow
http server is running...press enter key to exit.

cURLを使ってサーバに対してGETリクエストを行います。

C:\work\node>curl -v http://localhost:3000/.textfile.txt
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /.textfile.txt HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: public, max-age=0
< Last-Modified: Sun, 10 Jul 2016 04:52:16 GMT
< ETag: W/"38-155d326179b"
< Content-Type: text/plain; charset=UTF-8
< Content-Length: 56
< Date: Sun, 10 Jul 2016 05:24:54 GMT
< Connection: keep-alive
<
���これはテキストファイルの内容です。
* Failed writing body (22 != 56)
* Closing connection 0
curl: (23) Failed writing body (22 != 56)
C:\work\node>curl -v http://localhost:3000/
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET / HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: public, max-age=0
< Last-Modified: Sun, 10 Jul 2016 05:23:12 GMT
< ETag: W/"7-155d34267ac"
< Content-Type: text/plain; charset=UTF-8
< Content-Length: 7
< Date: Sun, 10 Jul 2016 05:25:36 GMT
< Connection: keep-alive
<
index
* Connection #0 to host localhost left intact
C:\work\node>curl -v http://localhost:3000/index2.txt
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /index2.txt HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< X-Content-Type-Options: nosniff
< Content-Type: text/html; charset=utf-8
< Content-Length: 116
< Date: Sun, 10 Jul 2016 05:25:59 GMT
< Connection: keep-alive
<
Error: ENOENT: no such file or directory, stat &#39;C:\work\node\index2.txt&#39;<br> &nbsp; &nbsp;at Error (native)
* Connection #0 to host localhost left intact

サーバから正しくレスポンスが返っていることが分かります。

Enterキーを押すとサーバが停止し、プログラムが終了します。

3つ目のコマンドライン引数にdenyを指定した場合には下のようになります。

C:\work\node>node express-server-folder.js deny
http server is running...press enter key to exit.
ForbiddenError: Forbidden
    at SendStream.error (C:\work\node\node_modules\send\index.js:245:31)
    at SendStream.pipe (C:\work\node\node_modules\send\index.js:490:21)
    at serveStatic (C:\work\node\node_modules\serve-static\index.js:124:12)
    at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\work\node\node_modules\express\lib\router\index.js:312:13)
    at C:\work\node\node_modules\express\lib\router\index.js:280:7
    at Function.process_params (C:\work\node\node_modules\express\lib\router\index.js:330:12)
    at next (C:\work\node\node_modules\express\lib\router\index.js:271:10)
    at C:\work\node\express-server-folder.js:30:2
    at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)

http server closed.
C:\work\node>curl -v http://localhost:3000/.textfile.txt
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /.textfile.txt HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< X-Content-Type-Options: nosniff
< Content-Type: text/html; charset=utf-8
< Content-Length: 947
< Date: Sun, 10 Jul 2016 05:27:14 GMT
< Connection: keep-alive
<
ForbiddenError: Forbidden<br> &nbsp; &nbsp;at SendStream.error (C:\work\node\node_modules\send\index.js:245:31)<br> &nbsp; &nbsp;at SendStream.pipe (C:\work\node\node_modules\send\index.js:490:21)<br> &nbsp; &nbsp;at serveStatic (C:\work\node\node_modules\serve-static\index.js:124:12)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)<br> &nbsp; &nbsp;at trim_prefix (C:\work\node\node_modules\express\lib\router\index.js:312:13)<br> &nbsp; &nbsp;atC:\work\node\node_modules\express\lib\router\index.js:280:7<br> &nbsp; &nbsp;at Function.process_params (C:\work\node\node_modules\express\lib\router\index.js:330:12)<br> &nbsp; &nbsp;at next (C:\work\node\node_modules\express\lib\router\index.js:271:10)<br> &nbsp; &nbsp;at C:\work\node\express-server-folder.js:30:2<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)
* Connection #0 to host localhost left intact

ignoreを指定した場合には下のようになります。

C:\work\node>node express-server-folder.js ignore
http server is running...press enter key to exit.
NotFoundError: Not Found
    at SendStream.error (C:\work\node\node_modules\send\index.js:245:31)
    at SendStream.pipe (C:\work\node\node_modules\send\index.js:493:21)
    at serveStatic (C:\work\node\node_modules\serve-static\index.js:124:12)
    at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)
    at trim_prefix (C:\work\node\node_modules\express\lib\router\index.js:312:13)
    at C:\work\node\node_modules\express\lib\router\index.js:280:7
    at Function.process_params (C:\work\node\node_modules\express\lib\router\index.js:330:12)
    at next (C:\work\node\node_modules\express\lib\router\index.js:271:10)
    at C:\work\node\express-server-folder.js:30:2
    at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)

http server closed.
C:\work\node>curl -v http://localhost:3000/.textfile.txt
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /.textfile.txt HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< X-Content-Type-Options: nosniff
< Content-Type: text/html; charset=utf-8
< Content-Length: 946
< Date: Sun, 10 Jul 2016 05:30:17 GMT
< Connection: keep-alive
<
NotFoundError: Not Found<br> &nbsp; &nbsp;at SendStream.error (C:\work\node\node_modules\send\index.js:245:31)<br> &nbsp; &nbsp;at SendStream.pipe (C:\work\node\node_modules\send\index.js:493:21)<br> &nbsp; &nbsp;at serveStatic (C:\work\node\node_modules\serve-static\index.js:124:12)<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)<br> &nbsp; &nbsp;at trim_prefix (C:\work\node\node_modules\express\lib\router\index.js:312:13)<br> &nbsp; &nbsp;at C:\work\node\node_modules\express\lib\router\index.js:280:7<br> &nbsp; &nbsp;at Function.process_params(C:\work\node\node_modules\express\lib\router\index.js:330:12)<br> &nbsp; &nbsp;at next (C:\work\node\node_modules\express\lib\router\index.js:271:10)<br> &nbsp; &nbsp;at C:\work\node\express-server-folder.js:30:2<br> &nbsp; &nbsp;at Layer.handle [as handle_request] (C:\work\node\node_modules\express\lib\router\layer.js:95:5)
* Connection #0 to host localhost left intact

.textfile.txtの読み取りが拒否に設定されている場合には下のようになります。

C:\work\node>type .textfile.txt
Access is denied.
C:\work\node>node express-server-folder.js allow
http server is running...press enter key to exit.
Error: EPERM: operation not permitted, open 'C:\work\node\.textfile.txt'
    at Error (native)

http server closed.
C:\work\node>curl -v http://localhost:3000/.textfile.txt
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /.textfile.txt HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 500 Internal Server Error
< Accept-Ranges: bytes
< Cache-Control: public, max-age=0
< Last-Modified: Sun, 10 Jul 2016 04:52:16 GMT
< ETag: W/"38-155d326179b"
< Content-Type: text/html; charset=utf-8
< Content-Length: 116
< X-Content-Type-Options: nosniff
< Date: Sun, 10 Jul 2016 05:46:03 GMT
< Connection: keep-alive
<
Error: EPERM: operation not permitted, open &#39;C:\work\node\.textfile.txt&#39;<br> &nbsp; &nbsp;at Error (native)
* Connection #0 to host localhost left intact

.textfile.txtが存在しない場合には下のようになります。

C:\work\node>type .textfile.txt
The system cannot find the file specified.
C:\work\node>node express-server-folder.js allow
http server is running...press enter key to exit.
Error: ENOENT: no such file or directory, stat 'C:\work\node\.textfile.txt'
    at Error (native)

http server closed.
C:\work\node>curl -v http://localhost:3000/.textfile.txt
*   Trying ::1...
* Connected to localhost (::1) port 3000 (#0)
> GET /.textfile.txt HTTP/1.1
> Host: localhost:3000
> User-Agent: curl/7.46.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< X-Content-Type-Options: nosniff
< Content-Type: text/html; charset=utf-8
< Content-Length: 119
< Date: Sun, 10 Jul 2016 05:49:23 GMT
< Connection: keep-alive
<
Error: ENOENT: no such file or directory, stat &#39;C:\work\node\.textfile.txt&#39;<br> &nbsp; &nbsp;at Error (native)
* Connection #0 to host localhost left intact

関連

pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-express, Node.js