Socket.ioを使ってサーバーの画像が更新されたらブラウザ側も更新させるメモ


画像ストリーミング表示っぽいものを作ってみます。

この辺りの続きです。 `Node.js v8.4.0`です

* [Node.jsで画像アップロードと表示](http://qiita.com/n0bisuke/items/7dc855fe5eb76fdf1ca9)
* [Node.jsで画像アップロードを受けつけるサーバー](http://qiita.com/n0bisuke/items/12a1d0fed0f544269489)

## とりあえずポーリングで対応

フロント側のJavaScriptで定期的に画像を更新するようにします。
`ctx.clearRect()`と`ctx.beginPath()`で描画をリセットできます。

(canvasあんまり触ってないから使い方怪しいかも)

```public/app.js
'use strict';

const canvas = document.getElementById('myCanvas');
if(!canvas || !canvas.getContext) console.log('canvasが使えないよ');

const draw = () => {
    const ctx = canvas.getContext('2d');
    const img = new Image();
    img.src = `img.jpeg?date=${new Date()}`;
    img.onload = () => ctx.drawImage(img, 0, 0);
    return ctx;
}

document.body.addEventListener('click',draw);
setInterval(()=>{
    const ctx = draw();
    ctx.clearRect(0,0,640,600);
    ctx.beginPath();
    console.log('更新');
}, 5000);
```

`img.src`のパス指定で`img.jpeg?date=${new Date()}`としていますが、おなじパスだとブラウザによってはキャッシュされてしまうことがあるのでおなじパスにならないようにしてます。

この状態だと結構カクカクします。

### CreateJSを利用して性能アップ

このカクカクする感じを解消しようと試してましたが、CreateJSを使ったらいい感じに表示されました。

```index.html
<html>
<head>
    <meta charset="utf-8" />
    <title>Nefry BT Camera</title>
</head>

<body style="background-color:#D0D0D0;">
    <canvas id="myCanvas" width="640" height="480" style="background-color:#FFFFFF;"></canvas>
    <script src="https://code.createjs.com/easeljs-0.8.2.min.js"></script>
    <script src="/public/app.js"></script>
</body>
</html>
```

コードも結構短くなった。

```public/app.js
'use strict';

const stage = new createjs.Stage('myCanvas');
const socket = io();

const draw = () => {
    const bmp = new createjs.Bitmap(`/img.jpeg?date=${new Date()}`);
    stage.addChild(bmp);
    createjs.Ticker.on('tick', () => stage.update());
    console.log(`update: ${new Date()}`);
}

setInterval(()=>draw(), 5000);
```

[ICSさんの記事](https://ics.media/tutorial-createjs/bitmap.html)が参考になりました。

## Socket.ioを使って画像変更を検知

現状だとポーリングなので余計なリクエストが発生してしまっています。

Socket.ioを使って画像変更を検知します。

>* [Node.jsで画像アップロードと表示](http://qiita.com/n0bisuke/items/7dc855fe5eb76fdf1ca9)
>* [Node.jsで画像アップロードを受けつけるサーバー](http://qiita.com/n0bisuke/items/12a1d0fed0f544269489)

↑の記事の続きなのでsocket.ioを追加でインストールします。

```
npm i --save socket.io
```

```server.js
'use strict';

const fs = require('fs');
const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);
const express = require('express');
const PORT = process.env.PORT || 3000;
app.use(express.static(__dirname));

io.on('connection', (socket) => console.log('a user connected')); //socket.ioのコネクション

app.get('/', (req, res) => res.sendFile('./index.thml'));
app.post('/', (req, res) => {
    let buffers = [];
    let cnt = 0;

    req.on('data', (chunk) => {
        buffers.push(chunk);
        console.log(++cnt);
    });

    req.on('end', () => {
        console.log(`[done] Image upload`);
        req.rawBody = Buffer.concat(buffers);
        //書き込み
        fs.writeFile('./img.jpeg', req.rawBody, 'utf-8',(err) => {
            if(err) return;
            console.log(`[done] Image save`);
            io.sockets.emit('new message',{message: `[done] Image save`}); //画像が更新されたことを通知
        });
    });
});
 
http.listen(PORT, () => console.log(`listening on *:${PORT}`));
```

```public/app.js
'use strict';

const stage = new createjs.Stage('myCanvas');
const socket = io();

const draw = () => {
    const bmp = new createjs.Bitmap(`/img.jpeg?date=${new Date()}`);
    stage.addChild(bmp);
    createjs.Ticker.on('tick', () => stage.update());
    console.log(`update: ${new Date()}`);
}

socket.on('new message', (msg) => draw());

draw(); //初期実行
```

サーバー側で画像の更新があったら`new message`のイベントが発火し、フロント側ではこのイベントをキャッチしたら画像の更新処理を行います。

これで画像がストリーム表示っぽくなりました。

## 所感

とりあえずカメラに写っている部屋などを外から監視する仕組みが作れそうです。
[`socket.ioで直接base64変換した画像データを送る`](http://irukanobox.blogspot.jp/2016/06/socketio_25.html)みたいな機能も検討したいです。