Angular与Nestjs的文件流传输(SSE)

概述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.readyState2(关闭)。

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'
  }
});
相关推荐
irving同学462386 天前
TypeORM 列装饰器完整总结
前端·后端·nestjs
Wang's Blog7 天前
Nestjs框架: 基于策略的权限控制(ACL)与数据权限设计
nestjs·rbac·acl
三十_7 天前
【NestJS】构建可复用的数据存储模块 - 动态模块
前端·后端·nestjs
SuperheroMan824667 天前
部署时报错:Type 'string' is not assignable to type 'never'(Prisma 关联字段问题)
nestjs
郭俊强9 天前
nestjs 缓存配置及防抖拦截器
缓存·nestjs·防抖
用户8001536355012 天前
在 Nest.js 中实现文件上传
nestjs
三十_13 天前
NestJS 开发必备:HTTP 接口传参的 5 种方式总结与实战
前端·后端·nestjs
关山月16 天前
使用Nest.js设计RBAC权限系统:分步指南
nestjs
百罹鸟17 天前
nestjs 从零开始 一步一步实践
前端·node.js·nestjs