2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Node.js: Server Sent Events のサンプル (画像表示)

Last updated at Posted at 2021-05-20

こちらのプログラムを簡単にしたサンプルを作成しました。
simonprickett/server-sent-events-demo

次のような出力を出すようにします。
server_sent_events_may20.png

サーバー

フォルダー構成

$ tree
.
├── package.json
└── server.js
server.js
// ---------------------------------------------------------------
//	server/server.js
//
//						May/20/2021
// ---------------------------------------------------------------
const http = require('http')
const PORT = process.env.PORT || 5000

const memes = [
  'https://ekzemplaro.org/tmp/images/a001.jpg',
  'https://ekzemplaro.org/tmp/images/a002.jpg',
  'https://ekzemplaro.org/tmp/images/a003.jpg',
  'https://ekzemplaro.org/tmp/images/a004.jpg',
  'https://ekzemplaro.org/tmp/images/a005.jpg',
  'https://ekzemplaro.org/tmp/images/a006.jpg',
  'https://ekzemplaro.org/tmp/images/a007.jpg'
]

// ---------------------------------------------------------------
function getRandomIndex(low, high) {
  const min = Math.ceil(low)
  const max = Math.floor(high)

  return Math.floor(Math.random() * (max - min + 1)) + min
}



// ---------------------------------------------------------------
function createRandomMemeMessage() {
  console.log('Sending meme message.')
  return (`event: meme\ndata:${memes[getRandomIndex(0, memes.length - 1)]}\n\n\n`)
}

// ---------------------------------------------------------------
function createRandomNamedEvents(request, response) {
  response.writeHead(200, {
    Connection: 'keep-alive',
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache'
  })

  let eventsSent = 1

  const interval = setInterval(() => {
    if (eventsSent === 31) {
      clearInterval(interval)
      console.log('Sent 30 events, stopping.')
      response.write('id: -1\ndata:\n\n\n')
      response.end()
      return
    }

    response.write(`id: ${eventsSent < 10 ? '0' : ''}${eventsSent}\n`)

        response.write(createRandomMemeMessage())
        eventsSent += 1
  }, 3000)

  request.on('close', () => {
    clearInterval(interval)
    response.end()
    console.log('Stopped sending events as client closed the connection.')
  })
}

http.createServer((request, response) => {
  response.setHeader('Access-Control-Allow-Origin', '*')
  response.setHeader('Access-Control-Request-Method', '*')
  response.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET')
  response.setHeader('Access-Control-Allow-Headers', '*')

  if (request.method === 'OPTIONS') {
    response.writeHead(200)
    response.end()
    return
  }

  switch (request.url) {
    case '/randomNamedEvents':
      createRandomNamedEvents(request, response)
      break
    default:
      // Unknown URL
      response.writeHead(404)
      response.end()
  }
}).listen(PORT)

console.log(`server-sent-events-demo server running on port ${PORT}`)
// ---------------------------------------------------------------
package.json
{
  "name": "sse-server-demo",
  "version": "0.0.1",
  "description": "Server Sent Events Demonstration Server",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/simonprickett/server-sent-events-demo.git"
  },
  "keywords": [
    "server-sent-events",
    "node"
  ],
  "author": "Simon Prickett",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/simonprickett/server-sent-events-demo/issues"
  },
  "homepage": "https://github.com/simonprickett/server-sent-events-demo#readme"
}

サーバーの起動

npm install
npm start

クライアント

以下のファイルを、Nginx または Apache2 の配下に置きます。

次のようにサーバーを動かす手もあります。

python -m http.server 8000


>フォルダー構成

>```text
$ tree
.
├── css
│   └── app.css
├── index.html
└── js
    └── app.js
index.html
<! DOCTYPE html>
<html>
<head>
<meta charset="utf-8"
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Server Sent Events Demo</title>
<link rel="stylesheet" href="css/app.css">
<script src="js/app.js"></script>
</head>
<body>
<div id="browserSupport" class="browserSupport">
</div>



<div id="meme" class="box memes">
                Meme Image Events
</div>
<div id="eventLog" class="rectangle eventLog">
                <!-- Event Log will go here -->
</div>
<hr />
May/20/2021 AM 06:57<br />
    </body>
</html>
js/app.js
// ---------------------------------------------------------------
//	client/js/app.js
//
//						May/20/2021
// ---------------------------------------------------------------
const app = {
  updateEventsReceived: (event) => {
    document.getElementById('eventLog').insertAdjacentHTML('afterbegin', `<br>${event.lastEventId}: (${event.type}) ${event.data}`);
  },

  resetStartButton: () => {
    document.getElementById('startEvents').value = 'Start Events';
  },

  startEvents: () => {
    app.eventSource = new EventSource('http://localhost:5000/randomNamedEvents');

    app.eventSource.onmessage = (e) => {
      console.log(e);
      if (e.lastEventId === '-1') {
        app.eventSource.close();
        document.getElementById('eventLog').insertAdjacentHTML('afterbegin', '<br>End of event stream from server.');
        app.resetStartButton();
      }
    };

    app.eventSource.addEventListener('meme', (e) => {
      console.log(e);
      document.getElementById('meme').innerHTML = `<img class="memeImage" src="${e.data}">`;
      app.updateEventsReceived(e);
    });

    app.eventSource.addEventListener('error', (e) => {
      console.log('got an error');
      console.log(e);
      app.resetStartButton();
    });
  }
};

document.addEventListener('DOMContentLoaded', () => {
  const browserSupportElem = document.getElementById('browserSupport');

  if (!! window.EventSource) {
    browserSupportElem.innerHTML = '<input type="submit" class="startEvents" id="startEvents" value="Start Events">';
//    browserSupportElem.innerHTML = 'This browser supports <tt>EventSource</tt>! <input type="submit" class="startEvents" id="startEvents" value="Start Events">';
    document.getElementById('startEvents').addEventListener('click', function(event) {
      if (this.value === 'Start Events') {
        this.value = 'Stop Events';
        app.startEvents();
      } else {
        app.eventSource.close();
        document.getElementById('eventLog').insertAdjacentHTML('afterbegin', '<br>Event stream closed by browser.');
        app.resetStartButton();
      }
    });
  } else {
    browserSupportElem.innerHTML = 'This browser does not support <tt>EventSource</tt> :(';
    browserSupportElem.classList.add('unsupportedBrowser');
  }
});

// ---------------------------------------------------------------
css/app.css
/* client/css/app.css */
html {
    background-color: #000000;
    color: #FFFFFF;
}

a {
    color: #EEEE00;
    text-decoration: none;
}

.header {
    text-align: center;
    font-size: 3.0em;
    margin-top: 15px;
}

.container {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    margin: auto;
    max-width: 1200px;
    width: 90%;
}

.browserSupport {
    width: 80%;
    height: 60px;
    max-width: 960px;
    text-align: center;
    border-radius: 5px;
    border-color: #FFFFFF;
    border-width: 1px;
    background-color: #00BB00;
    color: #FFFFFF;
    font-size: 2em;
    line-height: 60px;
    margin-bottom: 30px;
    margin-top: 30px;
    margin-left: auto;
    margin-right: auto;
}

.unsupportedBrowser {
    background-color: #EE0000;
}

.box {
    width: 180px;
    height: 180px;
    border-radius: 5px;
    border-style: solid;
    border-color: #FFFFFF;
    border-width: 1px;
    margin: .5em;
    padding: 1em;
    text-align: center;
    font-size: 2em;
    color: #FFFFFF;
}


.startEvents {
    border-color: #FFFFFF;
    border-radius: 5px;
    border-width: 1px;
    border-style: solid;
    padding: 5px;
    font-size: 0.75em;
    float: right;
    margin-top: 10px;
    margin-right: 20px;
}

.startEvents:active {
    background-color: #888888;
}

.memes {
    background-color: #FFEE00;
}

.memeImage {
    width: 100%;
    height: 100%;
}

.eventLog {
    font-family: monospace;
    font-size: 1.25em;
    height: 150px;
    width: 600px;
    border-radius: 5px;
    border-style: solid;
    border-color: #FFFFFF;
    border-width: 1px;
    margin: 20px;
    overflow-y: scroll;
    overflow-x: hidden;
    padding: 5px;
}
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?