expressの復習がてら、イラスト専用の掲示板を作成してみた

今回は、イラストのみに特化した掲示板を、node.js(express)の復習がてら作成する。

今回作成したプロジェクトは、以下のリポジトリに保管した。

github.com

準備

まず、本プロジェクトを作成するにあたって、以下の環境を用意しておく。

  • node.js(v18.6.0)
  • npm(8.13.2)
  • mysql(8.0.29)

その上で、専用のディレクトリを作成し、そのディレクトリ内に移動した。

そして、そのディレクトリ内に、以下の五種類をnpmを用いてインストールした。

  • express
  • ejs
  • bodyparser
  • multer
  • mysql

webデザイン

まず、以下の3つのejsファイルを作成した。

main.ejs

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>イラスト掲示板</title>
        <meta name="viewport" content="width=device-width">         <link rel="stylesheet" href="css/main.css">
        </head>
    <body>
        <div class="header">
            イラスト掲示板
        </div>
        <div class="sub">
            <div class="make">
                <a href="upload.html">テーマの投稿</a>
            </div>
            <ul>
                <li>1</li>
            </ul>
        </div>
        <div class="main">
            <div class="title">welcome!!</div>
            <h1>掲示板</h1>
            <div class="box2">
                <ul>
                    <li>1</li>
                </ul>
            </div>
        </div>
    </body>
</html>

contents.ejs

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>イラスト掲示板</title>
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" href="css/main.css">
        </head>
    <body>
        <div class="header">
            イラスト掲示板
        </div>
        <div class="sub">
            <div class="make">
                <a href="upload.html">テーマの投稿</a>
            </div>
            <ul>
                <li>1</li>
            </ul>
        </div>
        <div class="main">
            <div class="title">タイトル</div>
            <div class="box1">
                <div class="font-zone">
                    <b><font color="green">1.</font>名前-タイトル-xxxx-xx-xx</b>
                </div>
                <div class="image-zone">
                    <img src="images/icons/icon.png"  id="avatar" alt="">
                </div>
            </div>
            <div class="box2">
                <form action="/illustupload" method="post" enctype = "multipart/form-data">
                    <div class="fields-group">
                        <div class="fields"><input type="name" class="inputfield" name="password" placeholder="名前"></div>
                        <div class="fields"><input type="name" class="inputfield" name="password" placeholder="タイトル"></div>
                        <input type="file" name="file"  accept="image/*">
                    </div>
                    <input type="submit" class="submit-button" value="送信">
                </form>
            </div>
        </div>
    </body>
</html>

make.ejs

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>掲示板作成</title>
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" href="css/main.css">
        </head>
    <body>
        <div class="header">
            イラスト掲示板
        </div>
        <div class="sub">
            <div class="make">
                <a href="upload.html">テーマの投稿</a>
            </div>
            <ul>
                <li>1</li>
                <li>1</li>
            </ul>
        </div>
        <div class="main">
            <div class="title">タイトル</div>
            <form action="/illustupload" method="post">
                <div class="fields-group">
                    <div class="fields"><input type="name" class="inputfield" name="password" placeholder="掲示板名"></div>
                </div>
                <input type="submit" class="submit-button" value="作成">
            </form>
        </div>
    </body>
</html>

そして、これだけだとただ文字が並べられただけのページになるため、デザイン用のcssファイルを作成した。

main.css

@charset "UTF-8";

.boxA::after {
    content: "";
    display: block;
    clear: both;
}

body	{margin: 0;
	font-family: 'メイリオ', 'Hiragino Kaku Gothic Pro', sans-serif;
    background-color: #b0b0b0;
}

span{
    color:#727272;
}

.header{
    width: 100%;
    height: 50px;
    margin-right: 5px;
    font-size: 35px;
    color: #ffffff;
    font-weight: bold;
    text-align:center;
    background-color: #fff200;
}

.sub{float:left;
    width: 20%;
    padding: 10px;
    background-color: #ffffff;
}
.main{float:left;
    width: 60%;
    padding: 10px;
    background-color: #ffffff;
}

.title{
    font-size: 25px;
    text-align    : center;
    font-weight: bold;
    border-bottom: solid 2px #b0b0b0;
}
.box1{
    border-bottom: dotted 2px #727272;
}
.font-zone{
    color:#727272;
}

.fields-group{
    margin: 10px 0px;
}
.fields{
    padding:5px;
    border:1px solid #999
}
.inputfield{
    border: none; /*枠線非表示*/
    outline: none;
    font-size: 18px;
}
.submit-button {
    display       : inline-block;
    border-radius : 10%;          /* 角丸       */
    font-size     : 12pt;        /* 文字サイズ */
    text-align    : center;      /* 文字位置   */
    cursor        : pointer;     /* カーソル   */
    padding       : 5px 8px;   /* 余白       */
    background    : #999;     /* 背景色     */
    color         : #ffffff;     /* 文字色     */
    line-height   : 1em;         /* 1行の高さ  */
    opacity       : 1;           /* 透明度     */
    border        : 2px solid rgb(85, 85, 85);    /* 枠の指定 */
}
.submit-button:hover{
    opacity       : 0.8;         /* カーソル時透明度 */
}

最後に、これらを表示させるjsコードを以下のように作成した。

index.js

var express = require('express');
var ejs = require("ejs");
var app = express();

app.engine('ejs',ejs.renderFile);
app.use(express.static('public'));

// get
app.get("/", (req, res) => {
    res.render('main.ejs',{

    });
});

app.get("/contents", (req, res) => {
    res.render('contents.ejs',{

    });
});

app.get("/make", (req, res) => {
    res.render('make.ejs',{

    });
});

var server = app.listen(3000, () => {
    console.log('Port Number is 3000.');
})

この状態で、http://localhost:3000/にアクセスし、動作確認を行った。すると、以下のようなページが表示された。

続いて、コンテンツページを閲覧してみる。すると、このようなページを閲覧することができた。

最後に、掲示板作成ページを閲覧してみる。すると、掲示板名の入力欄と確定ボタンが表示されていることが確認できた。これで一通りwebデザインは完了した。

データベースの設定

次に、データベースの設計を行う。ここでは、MySQLを用いてデータベースを作成する。

まずは、mysqlを起動させ、mysqlにパスワードを用いてアクセスする。

そして、本プロジェクトで利用するデータベースを作成する。

ここから、CREATEで以下の2つのデータベースを作成した。

chatsデータベース

contentsデータベース

フォーム処理

掲示板作成、及び掲示板への投稿機能を設計する。

まず、index.jsに、以下のような記述を先頭部分に加えた。

var multer = require('multer');

var filename;

var storage = multer.diskStorage({
    //ファイルの保存先を指定
    destination: function(req, file, cb){
        cb(null, './public/images')
    },
    //ファイル名を指定
    filename: function(req, file, cb){
        var extention = file.originalname;
        filename = Date.now() + '.' + extention.split('.').pop();
        cb(null, filename)
    }
})

var upload = multer({storage:storage})

var mysql = require('mysql');

var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));

var mysql_setting = {
    host: 'localhost',
    user: 'root',
    password: '',
    database: 'image_chat'
};

次に、post処理を2つ加えた。

app.post("/upload_contents", upload.single('file'), (req, res) => {
    var date = new Date();

    var name = req.body.name;
    var title = req.body.title;
    var update_date = date.getFullYear()
    + '/' + ('0' + (date.getMonth() + 1)).slice(-2)
    + '/' + ('0' + date.getDate()).slice(-2)
    + ' ' + ('0' + date.getHours()).slice(-2)
    + ':' + ('0' + date.getMinutes()).slice(-2)
    + ':' + ('0' + date.getSeconds()).slice(-2);
    var chat_id = req.body.chat_id;
    var data = {'name':name, 'title':title, 'update_date': update_date, 'image_name':filename, 'chat_id':chat_id}

    var connection = mysql.createConnection(mysql_setting);

    connection.connect();
    connection.query('insert into contents set ?', data, function (error, results, fields){
        res.redirect('/ch'+chat_id);
    });

    connection.end();
});

app.post("/makechat", (req, res) => {
    var name = req.body.name;
    var data = {'name':name}

    var connection = mysql.createConnection(mysql_setting);

    connection.connect();
    connection.query('insert into chats set ?', data, function (error, results, fields){
        res.redirect('/');
    });

    connection.end();
});

これで、後はejs側の投稿フォームを変更すれば、投稿フォーム自体は完成する。

なお、掲示板作成の際は、chatsデータベースに入力した掲示板名を挿入するようにしており、掲示板への書き込みの際は、名前、タイトル、画像を選択し、それらをcontentsデータベースに挿入するだけでなく、投稿時のミリ秒と元画像の拡張子を挿入した画像名、掲示板ID、投稿日時をそれぞれcontentsデータベースに自動で挿入し、その画像名で、multerを用いてディレクトリに保存するようにしている。

投稿の表示

次は、データベースを経由することで掲示板やそれへの投稿の一覧を表示するようにする。

そのために、index.jsのgetリクエスト部分を、以下のように書き換えた。

app.get("/", (req, res) => {
    var connection = mysql.createConnection(mysql_setting);

    connection.connect();    
    connection.query('SELECT * from chats',function (error, results, fields){
        if (error == null){
            res.render('main.ejs',
            {
                chats:results
            });
        }
    });
    connection.end();
});

app.get("/ch:chatlink", (req, res) => {
    var chatlink = req.params.chatlink
    var chats
    var images

    var connection = mysql.createConnection(mysql_setting);

    connection.connect();  
    connection.query('SELECT * from chats',function (error, results, fields){
        if (error == null){
            chats=results
        }
    });
    connection.query('SELECT * from contents where chat_id= ?', chatlink,function (error, results, fields){
        if (error == null){
            images=results
        }
    });
    connection.query('SELECT * from chats where item_id= ?',chatlink ,function (error, results, fields){
        if (error == null){
            res.render('contents.ejs',{
                nowchat:results[0],
                chats:chats,
                images:images
            });
        }
    });
    connection.end();
});

app.get("/make", (req, res) => {
    var connection = mysql.createConnection(mysql_setting);

    connection.connect();
    connection.query('SELECT * from chats',function (error, results, fields){
        if (error == null){
            res.render('make.ejs',{
                chats:results
            });
        }
    });
    connection.end();
});

このようにすれば、掲示板の一覧や投稿の一覧が表示されるようになる。

因みに、各掲示板ページの表示に至っては、ch:chatlinkというように、ch(チャットID)にアクセスすることで、そのIDに対応したページにアクセスすることができるようにしている。

表示

index.jsの修正は終えたため、最後に各ejsファイルを以下のように書き換えた。

main.ejs

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>イラスト掲示板</title>
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" href="css/main.css">
        </head>
    <body>
        <div class="header">
            <a href="/" class="head">イラスト掲示板</a>
        </div>
        <div class="sub">
            <div class="make">
                <a href="/make">テーマの投稿</a>
            </div>
            <ul>
                <% for(var i in chats) { %>
                    <li><a href="/ch<%-chats[i].item_id%>"><%=chats[i].name%></a></li>
                <% } %>
            </ul>
        </div>
        <div class="main">
            <div class="title">welcome!!</div>
            <h1>掲示板</h1>
            <div class="box2">
                <ul>
                    <% for(var i in chats) { %>
                        <li><a href="/ch<%-chats[i].item_id%>"><%=chats[i].name%></a></li>
                    <% } %>
                </ul>
            </div>
        </div>
    </body>
</html>

contents.ejs

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title><%=nowchat.name%></title>
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" href="css/main.css">
        </head>
    <body>
        <div class="header">
            <a href="/" class="head">イラスト掲示板</a>
        </div>
        <div class="sub">
            <div class="make">
                <a href="/make">テーマの投稿</a>
            </div>
            <ul>
                <% for(var i in chats) { %>
                    <li><a href="/ch<%-chats[i].item_id%>"><%=chats[i].name%></a></li>
                <% } %>
            </ul>
        </div>
        <div class="main">
            <div class="title"><%=nowchat.name%></div>
            <% for(var i in images) { %>
                <div class="box1">
                    <div class="font-zone">
                        <b><font color="green"><%- i %>.</font><%=images[i].name%>-<%=images[i].title%>-<%=images[i].update_date%></b>
                    </div>
                    <div class="image-zone">
                        <img src="images/<%=images[i].image_name%>">
                    </div>
                </div>
            <% } %>
            <div class="box2">
                <form action="/upload_contents" method="post" enctype = "multipart/form-data">
                    <div class="fields-group">
                        <input type="hidden" name="chat_id" value="<%=nowchat.item_id%>">
                        <div class="fields"><input type="text" class="inputfield" name="name" placeholder="名前"></div>
                        <div class="fields"><input type="text" class="inputfield" name="title" placeholder="タイトル"></div>
                        <input type="file" name="file"  accept="image/*">
                    </div>
                    <input type="submit" class="submit-button" value="送信">
                </form>
            </div>
        </div>
    </body>
</html>

make.ejs

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <title>掲示板作成</title>
        <meta name="viewport" content="width=device-width">
        <link rel="stylesheet" href="css/main.css">
        </head>
    <body>
        <div class="header">
            <a href="/" class="head">イラスト掲示板</a>
        </div>
        <div class="sub">
            <div class="make">
                <a href="/make">テーマの投稿</a>
            </div>
            <ul>
                <% for(var i in chats) { %>
                    <li><a href="/ch<%-chats[i].item_id%>"><%=chats[i].name%></a></li>
                <% } %>
            </ul>
        </div>
        <div class="main">
            <div class="title">掲示板作成</div>
            <form action="/makechat" method="post">
                <div class="fields-group">
                    <div class="fields"><input type="text" class="inputfield" name="name" placeholder="掲示板名"></div>
                </div>
                <input type="submit" class="submit-button" value="作成">
            </form>
        </div>
    </body>
</html>

この状態で、先程のページにアクセスしてみる。すると、以下のようにサイドメニュー、本文に掲示板の一覧が表示されるようになった。

そして、次に掲示板作成を行う。「テーマの投稿」リンクから掲示板作成フォームに飛び、適当に掲示板名を打って作成ボタンを押す。

すると、掲示板一覧に「223」という名前の掲示板が追加された。

そして、そこをクリックし、アクセスしてみると、以下のようなページに飛んだ。そこで、名前、タイトル、画像を選択し、送信してみる。

すると、以下のように名前、タイトル、日時、投稿した画像が表示され、投稿機能が動作することも確認できた。このようにして、イラスト専用の掲示板を作成した。