xgqfrms™, xgqfrms® : xgqfrms's offical website of cnblogs! xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!

How to fix EventSource onmessage not working in JavaScript All In One

How to fix EventSource onmessage not working in JavaScript All In One

SSE: Server-Sent Events / 服务端推送

error ❌

window.addEventListener(`load`, (e) => {
  console.log(`page loaded ✅`);
  if (!!window.EventSource) {
    const img = document.querySelector(`#sse`);
    const source = new EventSource('http://localhost:3000/sse');
    source.onopen = (event) => {
      console.log(`✅ Connection to server opened.`, event);
    };
    source.onmessage = (event) => {
      const data = event.data;
      console.log(`🚀 data =`, data)
      // img.src = `data:image/jpg;base64,${data}`;
      // img.src = `data:image/png;base64,${data}`;
      img.src = `${data}`;
    };
    // source.addEventListener(`message`, (e) => {
    //   const data = e.data;
    //   console.log(`data =`, data)
    //   img.src = `data:image/png;base64,${data}`;
    // }, false);
    source.onerror = (err) => {
      console.log(`❌ EventSource failed.`, err);
      // setTimeout(() => {
      //   console.log(`⚠️ After 3 seconds, auto close connection!`);
      //   source.close();
      // }, 3000);
    };
  } else {
    console.log(`Your browser doesn't support SSE ❌`);
  }
});

image

EventSource: event.type type: "open" ⚠️

image

solutions ✅

使用 \n\n 换行符号,标识 stream 结束 ✅

import fs from 'node:fs';
import express from 'express';

const app = express();
app.use(express.static('public'));

app.get('/sse', (req, res) => {
  res.writeHead(200, {
    // 'Content-Type': 'image/jpg',
    // 'Content-Type': 'image/png',
    //   'Content-Type': 'application/json',
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  });
  let i = 0;
  let uid = setInterval(() => {
    console.log(`i`, i);
    if(i < 10) {
      i++;
      const data = convertImageToBase64URL("./public/test.png");
      console.log(`SSE ${i}`, data.slice(0, 100));
      // ❌
      // res.write(data);
      // ✅ 使用 \n\n 换行符号,标识 stream 结束 ❓
      // event type `message`
      // event data `data`
      res.write(`event: message\n`);
      res.write(`data: ${data}\n\n`);
    } else {
      clearInterval(uid);
      res.end();
    }
  }, 3000);
});
// http://localhost:3000/sse

const port = 3000;
app.listen(port, () => {
  console.log(`SSE server is running on: http://localhost:${port}/`);
});


image

EventSource: event.type & event.data


window.addEventListener(`load`, (e) => {
  console.log(`page loaded ✅`);
  if (!!window.EventSource) {
    const img = document.querySelector(`#sse`);
    const source = new EventSource('http://localhost:3000/sse');
    source.onopen = (event) => {
      console.log(`✅ Connection to server opened.`, event);
    };
    // ✅ event.type & event.data
    source.onmessage = (event) => {
      console.log(`event`, event);
      const type = event.type;
      console.log(`event.type`, type);
      const data = event.data;
      console.log(`🚀 event.data =`, data)
      // img.src = `data:image/jpg;base64,${data}`;
      // img.src = `data:image/png;base64,${data}`;
      img.src = `${data}`;
    };
    // custom-event-type-name ❓
    // source.addEventListener(`custom_event_type_name`, (e) => {
    //   const data = e.data;
    //   console.log(`data =`, data)
    //   img.src = `data:image/png;base64,${data}`;
    // }, false);
    source.onerror = (err) => {
      console.log(`❌ EventSource failed.`, err);
      // setTimeout(() => {
      //   console.log(`⚠️ After 3 seconds, auto close connection!`);
      //   source.close();
      // }, 3000);
    };
  } else {
    console.log(`Your browser doesn't support SSE ❌`);
  }
});

image

custom event type

server.js

app.get('/sse', (req, res) => {
  res.writeHead(200, {
    // 'Content-Type': 'image/jpg',
    // 'Content-Type': 'image/png',
    //   'Content-Type': 'application/json',
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    // 'CharacterEncoding': 'UTF-8',
    // 'Accept-Encoding': 'UTF-8',
  });
  // response.setContentType("text/event-stream");
  // response.setCharacterEncoding("UTF-8");
  // const data = convertImageToBase64URL("./public/test.png");
  // console.log(`SSE`, data.slice(0, 100));
  // res.write(data);
  let i = 0;
  let uid = setInterval(() => {
    console.log(`i`, i);
    if(i < 10) {
      i++;
      const data = convertImageToBase64URL("./public/test.png");
      console.log(`SSE ${i}`, data.slice(0, 100));
      // ❌
      // res.write(data);
      // ✅ 使用 \n\n 换行符号,标识 stream 结束 ❓
      // event type `message` \n
      // event data `data` \n\n
      // res.write(`event: message\n`);
      // custom event type `custom_message`\n ✅
      res.write(`event: custom_event_message\n`);
      res.write(`data: ${data}\n\n`);
    } else {
      clearInterval(uid);
      res.end();
    }
  }, 3000);
});
// http://localhost:3000/sse

client.js

window.addEventListener(`load`, (e) => {
  console.log(`page loaded ✅`);
  if (!!window.EventSource) {
    const img = document.querySelector(`#sse`);
    const source = new EventSource('http://localhost:3000/sse');
    source.onopen = (event) => {
      console.log(`✅ Connection to server opened.`, event);
    };
    // ✅ default event type `message` 可以使用 `onmessage` / addEventListener(`message`
    // source.onmessage = (event) => {
    //   console.log(`event`, event);
    //   const type = event.type;
    //   console.log(`event.type`, type);
    //   const data = event.data;
    //   console.log(`🚀 event.data =`, data)
    //   // img.src = `data:image/jpg;base64,${data}`;
    //   // img.src = `data:image/png;base64,${data}`;
    //   img.src = `${data}`;
    // };
    source.addEventListener(`message`, (event) => {
      console.log(`event`, event);
      const type = event.type;
      console.log(`event.type`, type);
      const data = event.data;
      console.log(`🚀 event.data =`, data)
      // img.src = `data:image/jpg;base64,${data}`;
      // img.src = `data:image/png;base64,${data}`;
      img.src = `${data}`;
    });
    // ❌ custom event message 不可以使用 `onmessage`
    // source.oncustom_event_message = (event) => {
    // ✅ custom event message 只可以使用 addEventListener(`custom_event_message`
    source.addEventListener(`custom_event_message`, (event) => {
      console.log(`event`, event);
      const type = event.type;
      console.log(`event.type`, type);
      const data = event.data;
      console.log(`🚀 event.data =`, data)
      // img.src = `data:image/jpg;base64,${data}`;
      // img.src = `data:image/png;base64,${data}`;
      img.src = `${data}`;
    });
    source.onerror = (err) => {
      console.log(`❌ EventSource failed.`, err);
      // setTimeout(() => {
      //   console.log(`⚠️ After 3 seconds, auto close connection!`);
      //   source.close();
      // }, 3000);
    };
  } else {
    console.log(`Your browser doesn't support SSE ❌`);
  }
});

image

event id lastEventId

app.get('/sse', (req, res) => {
  res.writeHead(200, {
    // 'Content-Type': 'image/jpg',
    // 'Content-Type': 'image/png',
    // 'Content-Type': 'application/json',
    // 'Content-Type': 'text/event-stream',
    'Content-Type': 'text/event-stream; charset=utf-8',
    // 'Content-Type': 'application/json; charset=utf-8',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    // 'Access-Control-Allow-Origin': "*",
    // 'CharacterEncoding': 'UTF-8',
    // 'Accept-Encoding': 'UTF-8',
  });
  // response.setContentType("text/event-stream");
  // response.setCharacterEncoding("UTF-8");
  // const data = convertImageToBase64URL("./public/test.png");
  // console.log(`SSE`, data.slice(0, 100));
  // res.write(data);
  let i = 0;
  let uid = setInterval(() => {
    console.log(`i`, i);
    if(i < 10) {
      i++;
      const data = convertImageToBase64URL("./public/test.png");
      console.log(`SSE ${i}`, data.slice(0, 100));
      // ❌
      // res.write(data);
      // event id `lastEventId` ✅
      const id = 2023;
      res.write(`id: ${id}\n`);
      // ✅ 使用 \n\n 换行符号,标识 stream 结束 ❓
      // event type `message` \n
      // event data `data` \n\n
      // res.write(`event: message\n`);
      // custom event type `custom_message`\n ✅
      res.write(`event: custom_event_message\n`);
      res.write(`data: ${data}\n\n`);
    } else {
      clearInterval(uid);
      res.end();
    }
  }, 3000);
});
// http://localhost:3000/sse


window.addEventListener(`load`, (e) => {
  console.log(`page loaded ✅`);
  if (!!window.EventSource) {
    const img = document.querySelector(`#sse`);
    const source = new EventSource('http://localhost:3000/sse');
    source.onopen = (event) => {
      console.log(`✅ Connection to server opened.`, event);
    };
    // ✅ default event type `message` 可以使用 `onmessage` / addEventListener(`message`
    // source.onmessage = (event) => {
    //   console.log(`event`, event);
    //   const type = event.type;
    //   console.log(`event.type`, type);
    //   const data = event.data;
    //   console.log(`🚀 event.data =`, data)
    //   // img.src = `data:image/jpg;base64,${data}`;
    //   // img.src = `data:image/png;base64,${data}`;
    //   img.src = `${data}`;
    // };
    source.addEventListener(`message`, (event) => {
      console.log(`event`, event);
      const type = event.type;
      console.log(`event.type`, type);
      const data = event.data;
      console.log(`🚀 event.data =`, data)
      // img.src = `data:image/jpg;base64,${data}`;
      // img.src = `data:image/png;base64,${data}`;
      img.src = `${data}`;
    });
    // ❌ custom event message 不可以使用 `onmessage`
    // source.oncustom_event_message = (event) => {
    // ✅ custom event message 只可以使用 addEventListener(`custom_event_message`
    source.addEventListener(`custom_event_message`, (event) => {
      console.log(`event`, event);
      const id = event.lastEventId;
      console.log(`event.lastEventId`, id);
      const type = event.type;
      console.log(`event.type`, type);
      const data = event.data;
      console.log(`🚀 event.data =`, data)
      // img.src = `data:image/jpg;base64,${data}`;
      // img.src = `data:image/png;base64,${data}`;
      img.src = `${data}`;
    });
    source.onerror = (err) => {
      console.log(`❌ EventSource failed.`, err);
      // setTimeout(() => {
      //   console.log(`⚠️ After 3 seconds, auto close connection!`);
      //   source.close();
      // }, 3000);
    };
  } else {
    console.log(`Your browser doesn't support SSE ❌`);
  }
});

image

image

Express.js & charset=utf-8

image

https://github.com/nodejs/node/issues/17390#issuecomment-1775500360

If you want Express to set a default charset (and you should!), use res.set('content-type') or res.type() to set the header.
A default charset will NOT be added when using res.setHeader().

Use req.accepts() instead.

req.accepts()
req.acceptsEncodings()
req.acceptsCharsets()
req.acceptsLanguages()

https://github.com/expressjs/express/wiki/Migrating from 3.x to 4.x#rescharset

https://expressjs.com/en/api.html#req.accepts

https://expressjs.com/en/api.html#req.acceptsCharsets

demos

Node.js SSE Server

// const express = require('express')
// const fs = require('fs')

import fs from 'node:fs';
import express from 'express';

const app = express();
app.use(express.static('public'));

// const convertImageToBase64URL = (filename, imageType = 'png') => {
//   try {
//     const base64String = fs.readFileSync(filename, {
//       encoding: 'base64',
//     });
//     console.log(`base64String`, base64String.slice(0, 100));
//     return `data:image/${imageType};base64,${base64String}`;
//   } catch (error) {
//     throw new Error(`file ${filename} no exist ❌`)
//   }
// }

const convertImageToBase64URL = (filename, imageType = 'png') => {
  try {
    const buffer = fs.readFileSync(filename);
    // ❌ 二合一 Buffer.from(buffer, 'base64')
    // const base64String = Buffer.from(buffer, 'base64');
    const base64String = Buffer.from(buffer).toString('base64');
    // console.log(`base64String`, base64String.slice(0, 100));
    return `data:image/${imageType};base64,${base64String}`;
  } catch (error) {
    throw new Error(`file ${filename} no exist ❌`)
  }
}

app.get('/img', (req, res) => {
  // res.writeHead(200, {
  //   'Content-Type': 'application/json',
  //   'Cache-Control': 'no-cache',
  //   'Connection': 'keep-alive',
  // });
  const data = convertImageToBase64URL("./public/test.png");
  // console.log(`base64String`, data.slice(0, 100));
  res.json({
    data,
  });
});
// http://localhost:3000/img

app.get('/sse', (req, res) => {
  res.writeHead(200, {
    // 'Content-Type': 'image/jpg',
    // 'Content-Type': 'image/png',
    //   'Content-Type': 'application/json',
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    // 'CharacterEncoding': 'UTF-8',
    // 'Accept-Encoding': 'UTF-8',
  });
  // response.setContentType("text/event-stream");
  // response.setCharacterEncoding("UTF-8");
  // const data = convertImageToBase64URL("./public/test.png");
  // console.log(`SSE`, data.slice(0, 100));
  // res.write(data);
  let i = 0;
  let uid = setInterval(() => {
    console.log(`i`, i);
    if(i < 10) {
      i++;
      const data = convertImageToBase64URL("./public/test.png");
      console.log(`SSE ${i}`, data.slice(0, 100));
      // ❌
      // res.write(data);
      // ✅ 使用 \n\n 换行符号,标识 stream 结束 ❓
      // event type `message`
      // event data `data`
      res.write(`event: message\n`);
      res.write(`data: ${data}\n\n`);
    } else {
      clearInterval(uid);
      res.end();
    }
  }, 3000);
});
// http://localhost:3000/sse


// app.get('/', (req, res) => {
//   res.file(`/public/index.html`);
// });
// http://localhost:3000/

const port = 3000;
app.listen(port, () => {
  console.log(`SSE server is running on: http://localhost:${port}/`);
});


(🐞 反爬虫测试!打击盗版⚠️)如果你看到这个信息, 说明这是一篇剽窃的文章,请访问 https://www.cnblogs.com/xgqfrms/ 查看原创文章!

Node.js Buffer.from


import fs from 'node:fs';

type Filename = string;
type ImageType = 'png' | 'jpg' | 'jpeg' | 'gif' | 'webp';
type Base64String = `data:image/${ImageType};base64,${string}`;

const convertImageToBase64URL = (filename: Filename, imageType: ImageType = 'png'): Base64String => {
  try {
    const buffer = fs.readFileSync(filename);
    const base64String = Buffer.from(buffer).toString('base64');
    // console.log(`base64String`, base64String.slice(0, 100));
    return `data:image/${imageType};base64,${base64String}`;
  } catch (error) {
    throw new Error(`file ${filename} no exist ❌`)
  }
}

export {
  convertImageToBase64URL,
};

// test cases
const ok = convertImageToBase64URL("./public/test.png");
const err = convertImageToBase64URL();
/* 

Expected 1-2 arguments, but got 0.ts(2554)
An argument for 'filename' was not provided.
const convertImageToBase64URL: (filename: Filename, imageType?: ImageType) => Base64String

*/

https://www.cnblogs.com/xgqfrms/p/17357737.html

PHP

sse-server.php

date_default_timezone_set("America/New_York");
header("Cache-Control: no-store");
header("Content-Type: text/event-stream");

$counter = rand(1, 10);
while (true) {
  // Every second, send a "ping" event.
  // 自定义事件名称 `ping` ✅ `event: ping\n`
  echo "event: ping\n";
  $curDate = date(DATE_ISO8601);
  echo 'data: {"time": "' . $curDate . '"}';
  // `\n\` stream 结束符号 ✅
  echo "\n\n";
  // Send a simple message at random intervals.
  $counter--;
  if (!$counter) {
    echo 'data: This is a message at time ' . $curDate . "\n\n";
    $counter = rand(1, 10);
  }
  ob_end_flush();
  flush();
  // Break the loop if the client aborted the connection (closed the page)
  if (connection_aborted()) break;
  sleep(1);
}

client.js

// 对于自定义事件名称,只能使用 addEventListener(`ping` 方式监听消息推送事件 ✅
evtSource.addEventListener("ping", (event) => {
  const newElement = document.createElement("li");
  const eventList = document.getElementById("list");
  const time = JSON.parse(event.data).time;
  newElement.textContent = `ping at ${time}`;
  eventList.appendChild(newElement);
});

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event

EventSource & default event type message

const sse = new EventSource("/api/v1/sse");


/*
The event "message" is a special case, as it will capture events without an event field as well as events that have the specific type `event: message` It will not trigger on any other event type.
 */
// 默认 SSE 事件名称 `notice` ✅
sse.addEventListener("message", (e) => {
  console.log(e.data);
});


/*
 * This will listen only for events similar to the following:
 *
 * event: notice 自定义 SSE 事件名称 `notice` ✅
 * data: useful data
 * id: someid
 */
sse.addEventListener("notice", (e) => {
  console.log(e.data);
});

/*
 * Similarly, this will listen for events with the field `event: update`
 */
// 自定义 SSE 事件名称 `update` ✅
sse.addEventListener("update", (e) => {
  console.log(e.data);
});


https://developer.mozilla.org/en-US/docs/Web/API/EventSource

HTTP/2

HTTPS

image

Event stream format

The event stream is a simple stream of text data which must be encoded using UTF-8. Messages in the event stream are separated by a pair of newline characters.
A colon as the first character of a line is in essence a comment, and is ignored.

事件流是一个简单的文本数据流,必须使用 UTF-8 进行编码。 事件流中的消息由一对换行符分隔。
作为一行的第一个字符的冒号本质上是注释,并且被忽略。

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format

The server-side script that sends events needs to respond using the MIME type text/event-stream. Each notification is sent as a block of text terminated by a pair of newlines. For details on the format of the event stream, see Event stream format.

发送事件的服务器端脚本需要使用 MIME 类型 text/event-stream 进行响应。每个通知都作为以一对换行符结尾的文本块发送。有关事件流格式的详细信息,请参阅事件流格式

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#sending_events_from_the_server

服务器发送消息,以 \n\n 分隔。

https://javascript.info/server-sent-events#server-response-format

refs

Node.js SSE

https://www.cnblogs.com/xgqfrms/p/17781678.html

https://stackoverflow.com/questions/77341435/nodejs-sending-image-files-using-sse/77342298#77342298

res.end();

https://stackoverflow.com/questions/36249684/simple-way-to-implement-server-sent-events-in-node-js/50594265#50594265

Express.js SSE

https://stackoverflow.com/questions/28673371/eventsource-onmessage-is-not-working-where-onopen-and-onerror-works-proper

https://stackoverflow.com/questions/59008192/server-sent-events-showing-blank-in-chrome-devtools-for-simple-express-sse/59128852#59128852

https://javascript.info/server-sent-events

https://stackoverflow.com/questions/17872789/node-js-express-how-do-i-set-response-character-encoding



©xgqfrms 2012-2021

www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!

原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!


posted @ 2023-10-23 22:43  xgqfrms  阅读(285)  评论(6编辑  收藏  举报