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 ❌`);
}
});
EventSource:
event.type
type: "open"
⚠️
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}/`);
});
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 ❌`);
}
});
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 ❌`);
}
});
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 ❌`);
}
});
Express.js &
charset=utf-8
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 typemessage
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
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 进行编码。 事件流中的消息由一对换行符
分隔。
作为一行的第一个字符的冒号
本质上是注释
,并且被忽略。
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
进行响应。每个通知都作为以一对换行符
结尾的文本块发送。有关事件流格式的详细信息,请参阅事件流格式
。
服务器发送消息,以 \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();
Express.js
SSE
https://javascript.info/server-sent-events
©xgqfrms 2012-2021
www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!
原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/17783689.html
未经授权禁止转载,违者必究!