使用 SseEmitter 实现 Spring Boot 后端的流式传输和前端的数据接收

1.普通文本消息的发送和接收

@GetMapping("/stream")public SseEmitter streamResponse() {SseEmitter emitter = new SseEmitter(0L); // 0L 表示永不超时Executors.newSingleThreadExecutor().execute(() -> {try {for (int i = 1; i <= 5; i++) {emitter.send("消息 " + i);Thread.sleep(1000); // 模拟延迟}emitter.complete();} catch (Exception e) {emitter.completeWithError(e);}});return emitter;}
async function fetchStreamData() {const response = await fetch("/api/chat/stream");// 确保服务器支持流式数据if (!response.ok) {throw new Error(`HTTP 错误!状态码: ${response.status}`);}const reader = response.body.getReader();const decoder = new TextDecoder("utf-8");// 读取流式数据while (true) {const { done, value } = await reader.read();if (done) break;// 解码并输出数据const text = decoder.decode(value, { stream: true });console.log("收到数据:", text);}console.log("流式传输完成");
}
// 调用流式请求
fetchStreamData().catch(console.error);

2.使用流式消息发送多个文件流,实现多个文件的传输

//这里相当于每个drawCatalogue对象会创建一个文件流,然后发送过去,list中有几个对象就会发送几个文件
//之所以要每个属性都手动write一下,是因为我的每个ajaxResult数据量都特别大,容易内存溢出。如果没有我这种特殊情况的话,直接使用JSONObject.toJSONString(drawCatalogue)就可以,不需要去手动写入每个属性。
public SseEmitter getAllDrawDataThree(String cadCode) {SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE); // 设置超时时间为最大值,防止自动结束try {Long code = Long.parseLong(cadCode);DrawExcelList drawExcelList = new DrawExcelList();drawExcelList.setCadCode(code);List<DrawCatalogue> drawCatalogueList = drawExcelListService.treeTableCatalogue(drawExcelList);int splitSize = 20;List<DrawCatalogue> newDrawCatalogueList = splitDrawCatalogueList(drawCatalogueList, splitSize);for (int i = 0; i < newDrawCatalogueList.size(); i++) {String filePath = "drawCatalogue" + i + ".json"; // 文件路径DrawCatalogue drawCatalogue = newDrawCatalogueList.get(i);try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {writer.write("["); // 开始写入最外层JSON数组writer.write("{");writer.write("\"id\": \"" + drawCatalogue.getId() + "\",");writer.write("\"drawName\": \"" + drawCatalogue.getDrawName() + "\",");writer.write("\"drawType\": \"" + drawCatalogue.getDrawType() + "\",");writer.write("\"combineOutType\": \"" + drawCatalogue.getCombineOutType() + "\",");writer.write("\"num\": \"" + drawCatalogue.getNum() + "\",");writer.write("\"children\": ");writer.write("["); // 开始写入childrenJSON数组boolean first = true; // 用于判断是否是第一个元素List<DrawCatalogue> children = drawCatalogue.getChildren();for (DrawCatalogue child : children) {DrawingMain drawingMain = new DrawingMain();drawingMain.setCadCode(code);drawingMain.setDrawName(child.getCombineOutType());drawingMain.setDrawType(child.getDrawType());AjaxResult ajaxResult = drawingMainService.imgListShow(drawingMain);if (!first) {writer.write(","); // 如果不是第一个元素,写入逗号分隔}String tabletJson = JSONObject.toJSONString(ajaxResult);// 逐个属性写入文件流writer.write("{");writer.write("\"id\": \"" + child.getId() + "\",");writer.write("\"drawName\": \"" + child.getDrawName() + "\",");writer.write("\"combineOutType\": \"" + child.getCombineOutType() + "\",");writer.write("\"drawType\": \"" + child.getDrawType() + "\",");writer.write("\"tabletJson\": " + tabletJson);writer.write("}");first = false; // 标记已经写入了一个元素}writer.write("]"); // 结束children数组writer.write("}");writer.write("]"); // 结束最外层JSON数组} catch (IOException e) {sseEmitter.completeWithError(e);}// 读取并发送文件流//byte[] fileData = Files.readAllBytes(Paths.get(filePath));// 分块读取文件并发送(防止一次性读取的文件过大导致内存溢出)Path path = Paths.get(filePath);ByteArrayOutputStream outputStream = new ByteArrayOutputStream();byte[] buffer = new byte[8192]; // 8KB buffertry (InputStream in = Files.newInputStream(path)) {int bytesRead;while ((bytesRead = in.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}}byte[] fileData = outputStream.toByteArray();sseEmitter.send(fileData, MediaType.APPLICATION_OCTET_STREAM);}sseEmitter.complete();} catch (Exception e) {sseEmitter.completeWithError(e);} finally {sseEmitter.complete();}return sseEmitter;}

前端代码,在方法中调用,后端返回几个文件就会弹出几个下载窗口

				const eventSource = new EventSource('http://127.0.0.1:1801/tablet/getAllDrawDataThree');eventSource.onmessage = function(event) {try {const fileData = event.data;const blob = new Blob([fileData], { type: 'application/octet-stream' });const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.style.display = 'none';a.href = url;a.download = 'file.json'; // 设置下载文件名document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url);document.body.removeChild(a);} catch (error) {console.error('Error processing event data:', error);}};eventSource.onerror = function(event) {console.error('SSE error:', event);};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

nssm配置springboot项目环境,注册为windows服务

NSSM 的官方下载地址是&#xff1a;NSSM - the Non-Sucking Service Manager1 使用powershell输入命令,java项目需要手动配置和依赖nacos .\nssm.exe install cyMinio "D:\minio\启动命令.bat" .\nssm.exe install cyNacos "D:\IdeaProject\capacity\nacos-s…

WinCC学习系列-基础概念

从本节起&#xff0c;学习和了解西门子最新SCADA软件WinCC 8.0&#xff0c;将从基础概念开始&#xff0c;到入门操作&#xff08;创建项目、组态通信、组态过程画面、组态面板类型和变量结构、归档和显示值、组态消息&#xff09;&#xff0c;到高级应用&#xff08;WinCC选件、…

数据分析图表类型及其应用场景

说明&#xff1a;顶部HTML文件下载后可以直接查看&#xff0c;带有示图。 摘要 数据可视化作为现代数据分析的核心环节&#xff0c;旨在将复杂、抽象的数据转化为直观、易懂的图形形式。这种转化显著提升了业务决策能力&#xff0c;优化了销售与营销活动&#xff0c;开辟了新…

《江西棒垒球》败方mvp叫什么·棒球1号位

败方mvp也是MVP&#xff0c;以棒球运动为例&#xff0c;MLB&#xff08;美国职棒大联盟&#xff09;的个人奖项旨在表彰球员在不同领域的卓越表现&#xff0c;涵盖常规赛和季后赛的杰出成就。 常规赛核心奖项 最有价值球员奖&#xff08;MVP&#xff09; 定义&#xff1a;表彰…

CD43.vector模拟实现(2)

目录 1.拷贝构造函数 写法1 写法2 测试代码 调试找bug 解决方法:修改拷贝构造函数 测试代码 2.operator[ ] 测试代码 1.没有const修饰 2.有const修饰 3.insert 迭代器失效问题 承接CD42.vector模拟实现(1)文章 1.拷贝构造函数 设置start、finish和end_of_storag…

【C/C++】入门grpc的idl

文章目录 grpc idl 简单介绍1. 文件结构组织规范文件命名包结构&#xff1a;推荐&#xff1a;一个文件只定义一个 service&#xff0c;如果 service 很复杂&#xff0c;可拆分多个 proto 文件。 2. 消息定义规范命名风格字段编号&#xff1a;示例&#xff1a; 3. 服务与 RPC 设…

安全-JAVA开发-第二天

Web资源访问的流程 由此可见 客户访问JAVA开发的应用时 会先通过 监听器&#xff08;Listener&#xff09;和 过滤器&#xff08;Filter&#xff09; 今天简单的了解下这两个模块的开发过程 监听器&#xff08;Listener&#xff09; 主要是监听 我们触发了什么行为 并进行反应…

使用 Ansys Q3D 进行电容提取

精确的电容提取在高速和 RF 设计中至关重要。虽然简单的公式可以提供一个很好的起点&#xff0c;但它们往往无法捕捉 fringing fields 和 layout-dependent parasitics 的影响。在本博客中&#xff0c;我们演示了如何使用Ansys Q3D Extractor来计算电容值&#xff0c;从基本的平…

卡西欧模拟器:Windows端功能强大的计算器

引言 大家还记得初中高中时期用的计算器吗&#xff1f;今天给大家分享的就是一款windows端的卡西欧计算器。 软件介绍 大家好&#xff0c;我是逍遥小欢。 CASIO fx-9860G是一款功能强大的图形计算器&#xff0c;适用于数学、科学和工程计算。以下是其主要功能和特点的详细介…

【Bluedroid】蓝牙启动之gatt_init 流程源码解析

本文围绕Android蓝牙协议栈中 GATT(通用属性配置文件)模块的初始化函数gatt_init展开,深入解析其核心实现逻辑与关键步骤。通过分析gatt_init及其关联子函数(如L2CA_RegisterFixedChannel、gatt_profile_db_init、EattExtension::Start等),以及相关数据结构(如tGATT_CB控…

Vue 3 中ref 结合ts 获取 DOM 元素的实践指南。

文章目录 前言一、为什么需要为 ref 添加类型&#xff1f;二、基本用法&#xff1a;引用 DOM 元素1. 引用通用 DOM 元素&#xff08;HTMLElement&#xff09;2. 引用特定类型的 DOM 元素&#xff08;如 HTMLDivElement&#xff09; 三、<script setup> 语法中的类型定义四…

Axure形状类组件图标库(共8套)

点击下载《月下倚楼图标库(形状组件)》 原型效果&#xff1a;https://axhub.im/ax9/02043f78e1b4386f/#g1 摘要 本图标库集锦精心汇集了8套专为Axure设计的形状类图标资源&#xff0c;旨在为产品经理、UI/UX设计师以及开发人员提供丰富多样的设计素材&#xff0c;提升原型设计…

01串(二进制串)与集合之间存在天然的对应关系 ← bitset

【集合的二进制表示‌】 ● 01 串&#xff08;二进制串&#xff09;与集合之间存在天然的对应关系。对应机理为每个二进制位可以表示集合中一个元素的存在&#xff08;1&#xff09;或不存在&#xff08;0&#xff09;。例如&#xff0c;集合 {a, b, c} 的子集 {a, c} 可以表示…

vba学习系列(10)--外观报表

系列文章目录 文章目录 系列文章目录前言一、外观报表1.产能统计2.单板数3.固定伤排查4.件号良率5.镜片批退率6.镜筒批退率 总结 前言 一、外观报表 1.产能统计 Sub ProcessInspectionData()Dim ws1 As Worksheet, ws2 As Worksheet, ws3 As WorksheetDim lastRow1 As Long, …

machine_env_loader must have been assigned before creating ssh child instance

在主机上执行roslaunch命令时&#xff0c;报错&#xff1a;machine_env_loader must have been assigned before creating ssh child instance。 解决办法&#xff1a; 打开hostos文件&#xff0c;检查local host 前的内部ip是否正常。操作示例&#xff1a; 先输入下方指令打…

CSS radial-gradient函数详解

目录 基本语法 关键参数详解 1. 渐变形状&#xff08;Shape&#xff09; 2. 渐变大小&#xff08;Size&#xff09; 3. 中心点位置&#xff08;Position&#xff09; 4. 颜色断点&#xff08;Color Stops&#xff09; 常见应用场景 1. 基本圆形渐变 2. 椭圆渐变 3. 模…

分析Web3下数据保护的创新模式

在这个信息爆炸的时代&#xff0c;我们正站在 Web3 的门槛上&#xff0c;迎接一个以去中心化、用户主权和数据隐私为核心的新时代。Web3 不仅仅是技术的迭代&#xff0c;它更是一场关于数据权利和责任的结构性变革。本文将探讨 Web3 下数据保护的创新模式&#xff0c;以期为用户…

RabbitMQ-Go 性能分析

更多个人笔记见&#xff1a; &#xff08;注意点击“继续”&#xff0c;而不是“发现新项目”&#xff09; github个人笔记仓库 https://github.com/ZHLOVEYY/IT_note gitee 个人笔记仓库 https://gitee.com/harryhack/it_note 个人学习&#xff0c;学习过程中还会不断补充&…

AI助力Java开发:减少70%重复编码,实战效能提升解析

工具再先进&#xff0c;也替代不了编程思维的深度锤炼 在Java开发领域&#xff0c;重复编码如同无形的生产力黑洞——以商品管理模块开发为例&#xff0c;开发者耗费大量时间编写SQL查询、处理结果集转换&#xff1b;用户系统里&#xff0c;密码加密和状态管理的代码在不同项目…

JS语法笔记

目录 JS数组Array新建数组一维数组二维数组 reverse()在数组末尾插入&#xff1a;push()在数组末尾删除&#xff1a;pop()在数组开头插入&#xff1a;unshift()从数组开头删除一个元素shift()splice() MapSet JS数组Array 判断数组相等不能用&#xff0c;要循环判断 新建数组…