概述SSE
(Server-Sent Events)是一种用于实现服务器主动向客户端推送数据的技术,也被称为"事件流"(Event Stream)。它基于 HTTP 协议,利用了其长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。
实例
使用生成事件的脚本 URL 创建一个 EventSource
对象,用来开启与服务器的连接并从中接收事件。
TS
const eventSource = new EventSource(url, configuration);
-
url:一个USVString ,它代表远程资源的位置
-
configuration : 为配置新连接提供选项:
withCredentials
,默认为false
,指示 CORS 是否应包含凭据 ( credentials )。
属性
eventSource.readyState
:只读属性 返回一个表示连接状态的数字
0
--- 连接1
- 打开2
--- 关闭
eventSource.url
:只读属性 返回一个表示源 URL 的字符串。
eventSource.withCredentials
:只读属性 返回一个布尔值,指示该EventSource
对象是否是使用 CORS 凭据集实例化的。
方法
当通过事件源接收到数据时,将触发 APImessage
的事件
Ts
interface MessageEvent{
data:string,//消息发送器发送的数据。
origin,//表示消息发射器来源的字符串。
lastEventId,//表示事件的唯一 ID 的字符串。
source,//代表消息发射器的A `MessageEventSource`(可以是WindowProxy、MessagePort)或对象ServiceWorker
ports//表示与发送消息的通道关联的端口的对象数组(在适当的情况下,例如在通道消息传递中或向共享工作线程发送消息时)。
}
evtSource.onmessage = function (event:MessageEvent) {}
当打开与事件源的连接时,将触发 APIopen
的事件
javascript
evtSource.addEventListener("open", (e) => {
console.log("The connection has been established.");
});
当与事件源的连接无法打开时,会触发 APIerror
的事件
Ts
//当发生错误时(例如网络超时或与访问控制有关的问题),会生成一个错误事件。
evtSource.onerror = (err) => {
console.error("EventSource failed:", err);
};
该接口close()
的方法EventSource
会关闭连接(如果已建立),并将属性设置 EventSource.readyState
为2
(关闭)。
Ts
//关闭事件流
//默认情况下,如果客户端和服务器之间的连接关闭,则连接将重新启动。
evtSource.close();
nestjs后端
今天公司有一个新的需求就是将一个40M的json文件发送到前端中去,这时就可以使用SSE进行流传输了,切片+压缩+流传输 搞定!!!
Ts
sseV2(@Res() res: FastifyReply): Observable<MessageEvent> {
const faguiJson = join(process.cwd(), './src/xiaoxiao.json');//数据就是一个普通的json数据
const readJson = fs.readFileSync(faguiJson, 'utf-8');
const jsonData = JSON.parse(readJson);
const eventChunks = jsonData.data;
const chunkSize = 1; // 每次发送的数据块大小
//要是有更好的方案 各位掘友们一定要提点提点我丫 我实在是想不出更快的方案了
return interval(0).pipe(
take(Math.ceil(eventChunks.length / chunkSize)),
map((index) => {
const start = index * chunkSize;
const end = Math.min(start + chunkSize, eventChunks.length);
const chunk = eventChunks.slice(start, end);
const compressedData = pako.gzip(JSON.stringify(chunk));
const base64Data = Buffer.from(compressedData).toString('base64');
return {
data: base64Data,
};
}),
);
}
Angular前端
刚开始用angular是实在不适应(而且国内文档教程极其少)但用着用着还真香了,当然了要是搭建一个小项目用vue就足够了,angular还是比较适合大型项目
这里我就直接贴代码了
Ts
private eventSource!: EventSource;
constructor( private ngZone: NgZone) {}
ngOnInit(): void {
this.eventSource = new EventSource('/api/item/sseV2');
this.eventSource.onmessage = (event) => {
const base64EncodedData = event.data;
// 将base64编码的数据转换成原始的压缩数据
const compressedData = new Uint8Array(
atob(base64EncodedData)
.split('')
.map((char) => char.charCodeAt(0))
);
// 对压缩数据进行解压缩处理
const data = JSON.parse(pako.ungzip(compressedData, { to: 'string' }));
//这里有个坑 如果不提醒视图需要更新的话是不会分段加载数据的
// 有没有大佬能指点下 有没有更好的方案 让视图更新
this.ngZone.run(() => {
this.receivedData.push(...data);
});
};
this.eventSource.onerror = (error) => {
console.log(error);
console.log('An error occurred while attempting to connect.');
this.eventSource.close();
};
}
当然还是有一个问题,EventSource
不支持传入headers
,也就无法传给服务端token
。这时候 EventSourcePolyfill
就来了
Ts
var es = new EventSourcePolyfill('/events', {
headers: {//设置你所需要的header
'X-Custom-Header': 'value'
}
});