のらぬこの日常を描く

ノージャンルのお役立ち情報やアニメとゲームの話、ソフトウェア開発に関する話などを中心としたブログです。

nodejs+expressでオレオレあぷろだを作る ~ 其の二

こんにちは。のらぬこです。

1週間ほど前にこんな記事を書きました。

noranuk0.hatenablog.com

ゲーム機等で撮ったスクショ画像を自PCに転送するために、オレオレあぷろだを自作して、ゲーム機のWebブラウザから自PCにファイルをアップロードする方法を書いた記事です。

前回の記事で、nodejsのexpressにmulterという拡張モジュールを追加してこんな感じのものを作りました。

f:id:noranuk0:20161118013335p:plain

まあ、一応動いてはいるんですが、あぷろだを名乗ってるのにファイルリストも見られないのはちょっといただけません。

今回は、前回作ったあぷろだもどきを拡張して、アップロード済のファイルリストを見られるようにしてみます。

前準備

前回の記事を参考に、冒頭に載せたページが表示できるところまで実装を進めてください。

今回作ったもの

今回作ったのは、下の画面のようなものになります。

f:id:noranuk0:20161126184516p:plain

前回の作った画面と比べると、登録されているファイル一覧が追加されています。

とりあえずソース

とりあえず、前回記事で作った環境にこのソースコピペして上書きしたら動くよっての貼っときます。

// package.json
{
  "name": "uploader",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.15.2",
    "cookie-parser": "~1.4.3",
    "debug": "~2.2.0",
    "express": "~4.14.0",
    "jade": "~1.11.0",
    "morgan": "~1.7.0",
    "serve-favicon": "~2.3.0",
    "multer": "~1.2.0",
    "date-utils": "1.2.21"
  }
}
// ./app.js

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var dateUtils = require('date-utils');
var fs = require('fs');

var multer = require('multer');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));


app.get('/', function(req, res){
    fs.readdir('./uploads', function(error, list) {
        if (error) {
            // TODO:
        } else {
            res.render('index', {'files' : list});
        }
    });
});

var upload = multer({ storage:
    multer.diskStorage({
      destination: function (req, file, cb) {
            cb(null, './uploads');
        },
      filename: function (req, file, cb) {
          var dt = new Date();
          var formatted = dt.toFormat("YYYYMMDD_HH24MISS");
          cb(null, '[' + formatted + ']' + file.originalname);
        }
    })
});
app.post('/', upload.single('uploadedfile'), function (req, res) {
    console.log(req.file);
  res.redirect(301, '/');
});

app.get('/files/:file', function(req, res){
    fs.readFile('./uploads/' + req.params.file,
        function(err, data) {
        res.send(data, 200);
    });
});


// catch 404 and forward to error handler
app.use(function(req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;
// ./views/index.jade

extends layout

block content
  h1 ファイルの登録
  form(method="post", enctype="multipart/form-data", action="/")
    input(type="file", name="uploadedfile")
    input(type="submit")

  h1 登録されているファイル
  ul
    for val in files
      li
        a(href="files/#{val}") #{val}

動かし方は、前回同様

npm install
npm start

です。
サーバが起動したら、ブラウザなどから http://{ip address}3000/ に繋いでください。

補足説明とか

前回からの変更点中心に補足説明です。

前回からの変更点1

保存するファイル名に入れていた日付をYYYYMMDD_HH24MISS の形式にしました。

          var dt = new Date();
          var formatted = dt.toFormat("YYYYMMDD_HH24MISS");
          cb(null, '[' + formatted + ']' + file.originalname);

このために、date-utils というパッケージを新たに追加しています。

前回からの変更点2

保存済のファイル一覧を表示するようにしました。

app.get('/', function(req, res){
    fs.readdir('./uploads', function(error, list) {
        if (error) {
            // TODO:
        } else {
            res.render('index', {'files' : list});
        }
    });
});

ディレクトリ内のファイルを列挙するのに fs というモジュール使ってます。nodejs 組み込みなので、package.json 書いて npm install しなくても使えます。

fs には、同期版(処理が終わるまでメソッドから戻らない)と、非同期版(処理が終わるのを待たずにメソッドから戻る。処理が終わったらコールバックが呼ばれる)の2つがありますが、今回は非同期版を使っています。

fileの一覧を、jadeに files という名前で渡しています。

jade側で、これを変数のように使うことができます

    for val in files
      li
        a(href="files/#{val}") #{val}

前回からの変更点3

保存済みファイルをのリンククリックでファイルをダウンロードできるようにしました。
自分のPCに上がっているファイルをわざわざブラウザから見る機会なんてそうそうないとは思いますが。

app.get('/files/:file', function(req, res){
    fs.readFile('./uploads/' + req.params.file,
        function(err, data) {
        res.send(data, 200);
    });
});

リクエストルールを記載する部分に :file のように書けば、リクエストURLの対象部分が、 req.params.file のような形で取り出すことができます。

また、正規表現で縛ることもできて、その場合には :file([A-Z0-9]{8}\.[A-Z0-9]{3}) のように書きます。

メソッドの中身は、対象のファイルを全部読み込んで、読み込んだデータをそのままレスポンスとして返しているだけです。

基本用途が画像のみなので取り敢えずこんな感じの実装にしましたが、例えば、ファイルサイズが10Gbと借る場合、それを一旦全部メモリーに読み込むことになるので、用途によってはもうちょっとどうにかしたほうが良い(というかすべき)と思います。

詳細な解説、説明などは、公式ドキュメントやQiitaなどの記事を参考にされるとよいかと思います。

今回の記事は以上となります。

お読みいただいてありがとうございました。