当前位置: 首页 > news >正文

使用Spring Boot、VUE实现SSE长连接:跟踪文件上传和任务进度

使用Spring Boot实现SSE长连接:跟踪文件上传和任务进度

文章目录

  • 使用Spring Boot实现SSE长连接:跟踪文件上传和任务进度
    • 什么是SSE?
    • 使用场景
    • 前端库选择
      • 安装`event-source-polyfill`
        • 1. 创建SSE连接
        • 2. 关闭SSE连接
        • 3. 结合Vue.js使用
    • 使用Spring Boot实现SSE
      • 1. 创建SSE工具类
      • 2. 实现文件上传进度通知
      • 3. 实现任务执行进度跟踪

在现代Web应用中,服务器实时向客户端推送数据是一项非常常见的需求。在多种实现技术中,Server-Sent Events(SSE)是一个轻量级的解决方案,适用于对实时性要求不高、数据量不大的场景。本文将介绍如何在Spring Boot中使用SSE,结合实际案例展示在文件上传和任务执行中的应用。

什么是SSE?

Server-Sent Events(SSE)是HTML5标准的一部分,允许服务器单向推送消息到客户端。它与WebSocket不同,SSE只支持服务器向客户端推送数据,而不支持客户端向服务器发送数据。SSE的优点在于其实现简单、兼容性好,非常适合不需要双向通讯的场景。

使用场景

  • 文件上传进度通知:当用户上传文件时,服务器可以通过SSE实时告知客户端上传进度。
  • 任务执行进度跟踪:对于耗时的任务(如数据处理、批量导入等),可以通过SSE向客户端实时推送任务进度。

前端库选择

由于原生的EventSource在某些浏览器中可能不支持自定义请求头,因此选择event-source-polyfill库来建立SSE连接。这一库允许在初始化时设置请求头,如身份验证所需的token等。

安装event-source-polyfill

首先安装event-source-polyfill库:

npm install event-source-polyfill

前端实现步骤

1. 创建SSE连接

通过封装一个createSseConnection函数,建立与服务端的SSE连接,并定义如何处理不同消息事件:

import { EventSourcePolyfill } from "event-source-polyfill";
import config from "../../../../config";export function createSseConnection(context, topic, callbacks, showMessage, onError) {const url = config.sse_host[process.env.NODE_ENV] + "/techik/sse/subscribe?topic=" + topic;const headers = { "token": localStorage.getItem("token") };const source = new EventSourcePolyfill(url, {headers,heartbeatTimeout: 30 * 60 * 1000,});source.onopen = () => {console.log("SSE connection established.");};source.onmessage = (e) => {const message = e.data;if (callbacks.onMessage) {callbacks.onMessage(message, context);}if (showMessage && message.includes('success')) {context.$message({type: "success",duration: 3000,message: "提交成功!",});}};source.onerror = (e) => {console.error("SSE error:", e);if (callbacks.onError) {callbacks.onError(e, context);}if (e.readyState === EventSource.CLOSED) {console.log("SSE connection closed.");if (callbacks.onClose) {callbacks.onClose(context);}} else if (onError) {onError(e);}};return source;
}
2. 关闭SSE连接

提供一个closeSseConnection函数,当不再需要接收消息时,手动关闭SSE连接:

export function closeSseConnection(source, context, afterClose) {if (source) {source.close();console.log("SSE connection closed.");if (afterClose) {afterClose(context);}}
}
3. 结合Vue.js使用

在Vue组件中使用createSseConnection和closeSseConnection管理SSE连接:

export default {data() {return {sseSource: null,};},methods: {startListening() {const topic = "uploadProgress"; // 根据需求选择不同的topicthis.sseSource = createSseConnection(this, topic, {onMessage: this.handleMessage,onError: this.handleError,onClose: this.handleClose,}, true);},handleMessage(message, context) {console.log("Received message:", message);// 处理接收到的消息},handleError(error, context) {console.error("Error received:", error);},handleClose(context) {console.log("Connection closed.");},stopListening() {closeSseConnection(this.sseSource, this, () => {this.sseSource = null;});}},beforeDestroy() {this.stopListening();}
};

使用Spring Boot实现SSE

1. 创建SSE工具类

首先,创建一个工具类SseUtils来管理SSE连接:

package com.techik.Util;import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;@Slf4j
public class SseUtils {// 原子计数器,用于跟踪活跃的连接数private static final AtomicInteger COUNT = new AtomicInteger(0);// 存储主题和对应的SseEmitter映射关系,确保线程安全private static final Map<String, SseEmitter> SSE_EMITTER_MAP = new ConcurrentHashMap<>();/*** 建立新的SSE连接,并设置相关的回调函数* * @param topic 要连接的主题* @return 新创建的SseEmitter对象*/public static SseEmitter connect(String topic) {// 设置超时时间为30分钟SseEmitter sseemitter = new SseEmitter(30 * 60 * 1000L);// 设置连接完成后的回调sseemitter.onCompletion(completionCallBack(topic));// 设置连接出错时的回调sseemitter.onError(errorCallBack(topic));// 设置连接超时时的回调sseemitter.onTimeout(timeoutCallBack(topic));// 将新的SseEmitter存储到Map中SSE_EMITTER_MAP.put(topic, sseemitter);// 增加活跃连接数COUNT.incrementAndGet();log.info("创建新的sse连接,当前的主题:{}", topic);return sseemitter;}/*** 发送消息到指定的主题* * @param topic   目标主题* @param message 要发送的消息内容*/public static void sendMessage(String topic, String message) {if (SSE_EMITTER_MAP.containsKey(topic)) {try {// 发送消息SSE_EMITTER_MAP.get(topic).send(message);} catch (IOException e) {log.error("当前的主题:{},发送消息-错误:{}", topic, e.getMessage());}}}/*** 移除指定主题的连接* * @param topic 要移除的主题*/public static void removeTopic(String topic) {// 从Map中移除SseEmitterSSE_EMITTER_MAP.remove(topic);// 减少活跃连接数COUNT.decrementAndGet();log.info("删除主题:{}", topic);}// 创建连接完成的回调函数private static Runnable completionCallBack(String topic) {return () -> {log.info("结束连接,{}", topic);removeTopic(topic);};}// 创建连接超时的回调函数private static Runnable timeoutCallBack(String topic) {return () -> {log.info("连接超时,{}", topic);removeTopic(topic);};}// 创建连接出错的回调函数private static Consumer<Throwable> errorCallBack(String topic) {return throwable -> {log.error("连接异常,{}", topic);removeTopic(topic);};}
}

2. 实现文件上传进度通知

在上传文件的过程中,可以使用SseEmitter向客户端实时推送上传进度:

@PostMapping("/upload")
public ResponseEntity<String> uploadFile(MultipartFile file) {String topic = "uploadProgress";SseEmitter emitter = SseUtils.connect(topic);// 模拟上传文件并推送进度for (int i = 0; i <= 100; i += 10) {SseUtils.sendMessage(topic, "上传进度: " + i + "%");Thread.sleep(500); // 模拟耗时操作}SseUtils.sendMessage(topic, "上传完成!");return ResponseEntity.ok("文件上传成功");
}

3. 实现任务执行进度跟踪

类似文件上传,当需要执行耗时任务时,可以使用SSE推送任务进度:

@GetMapping("/executeTask")
public ResponseEntity<String> executeTask() {String topic = "taskProgress";SseEmitter emitter = SseUtils.connect(topic);// 模拟任务执行并推送进度for (int i = 0; i <= 100; i += 20) {SseUtils.sendMessage(topic, "任务进度: " + i + "%");Thread.sleep(1000); // 模拟耗时操作}SseUtils.sendMessage(topic, "任务完成!");return ResponseEntity.ok("任务执行成功");
}

相关文章:

使用Spring Boot、VUE实现SSE长连接:跟踪文件上传和任务进度

使用Spring Boot实现SSE长连接&#xff1a;跟踪文件上传和任务进度 文章目录 使用Spring Boot实现SSE长连接&#xff1a;跟踪文件上传和任务进度什么是SSE&#xff1f;使用场景前端库选择安装event-source-polyfill1. 创建SSE连接2. 关闭SSE连接3. 结合Vue.js使用 使用Spring B…...

计算机网络技术基础:3.计算机网络的拓扑结构

网络拓扑结构是指用传输媒体互连各种设备的物理布局&#xff0c;即用什么方式把网络中的计算机等设备连接起来。将工作站、服务站等网络设备抽象为点&#xff0c;称为“节点”&#xff1b;将通信线路抽象为线&#xff0c;称为“链路”。由节点和链路构成的抽象结构就是网络拓扑…...

go-zero(十二)消息队列

go zero 消息队列 在微服务架构中&#xff0c;消息队列主要通过异步通信实现服务间的解耦&#xff0c;使得各个服务可以独立发展和扩展。 go-zero中使用的队列组件go-queue&#xff0c;是gozero官方实现的基于Kafka和Beanstalkd 的消息队列框架,我们使用kafka作为演示。 一、…...

会议通知:人工智能通识教育与实践发展暨和鲸科技AI通识课解决方案发布会

今年秋季学期起&#xff0c;全国多所高校面向本科生开设人工智能通识课。 当前人工智能通识课程的建设进展主要分为三种情况&#xff1a; 全市统筹&#xff0c;由某头部高校牵头建设市级人工智能通识课&#xff0c;以北京市、天津市为代表&#xff1b; 已于秋季学期按照课程…...

UDS自动化测试-Service 0x27(CAPL调用dll实现key计算)

文章目录 关联文章一、CANoe加载诊断数据库cdd、dll文件二、CAPLdiagGenerateKeyFromSeed关联文章 UDS - 深论Security Access Service 27服务-安全访问状态转换 CDD文件——CANdelaStudio Vector——CAPL语言设计 CANoe诊断测试 相信读者基于Diagnostic/ISO TP Confighratio…...

订单编号如何实现

背景 常见的订单编号是带有一些信息的&#xff0c;比如说创建日期例如&#xff1a;本案例中的订单日期 自增编号日期可以使用格式化字符串&#xff0c;自增则可以使用redis来实现 代码实现 redis就有自增的方法 每天的key都是不一样的&#xff0c;且过期时间设置为1天 // 生成…...

Vue3 大事件管理系统

Vue3 项目实战&#xff1a; &#x1f197;好久没有更新blog&#xff0c;最近在找工作&#xff0c;还有准备考试&#xff0c;哎&#xff0c;&#x1f636;‍&#x1f32b;️爆炸的大环境&#x1f972; 内卷开始&#x1f32f;&#x1f32f; 本篇文章涉及的技术栈速通链接&#x…...

IOS通过WDA自动化中遇到的问题

IOS自动化遇到的问题 搭建WDA环境中遇到的问题1、XCode unsupport iphone xxx.2、创建Bundle Identifier出现问题&#xff1a;Communication with Apple failed3、创建Bundle Identifier出现问题&#xff1a;Automatic signing failed \Signing certificate is invalid4、创建B…...

单独测试 pyautogui 的鼠标点击功能,确保它能够在当前环境中正常工作,鼠标自动点击的录制回放功能

感谢您提供的详细日志信息。根据您的反馈&#xff0c;问题可能出在 pyautogui 没有正确获取鼠标焦点或无法在预期的位置执行点击操作。我们将采取以下步骤来进一步诊断和解决这个问题&#xff1a; 1. **确保 pyautogui 正确执行点击操作**&#xff1a; - 我们将添加更多的调…...

路由引入问题(双点双向路由回馈问题)

简介 总所周知&#xff0c;路由引入import又称路由重分发redistribute&#xff0c;为了解决不同路由协议进程间路由信息不互通而使用的技术&#xff0c;由于不同路由协议的算法、机制、开销等因素的差异&#xff0c;它们之间无法直接交换路由信息。因此&#xff0c;路由引入技…...

设计模式之 适配器模式 C# 范例

在 C# 中&#xff0c;适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;旨在将一个类的接口转换成客户端所期待的另一个接口。适配器模式允许你将现有的类包装起来&#xff0c;使其能够与其他接口兼容。 适配器模式的使用场景&#xff1a; …...

LabVIEW实现GPS通信

目录 1、GPS通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…...

[leetcode100] 543. 二叉树的直径

https://leetcode.cn/problems/diameter-of-binary-tree/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目描述&#xff1a;给一个二叉树&#xff0c;返回二叉树直径最大值。直径指的是二叉树中任意一个结点到另外一个结点产生路径的长度。而长度由边来代表。…...

嵌入式学习(18)-stm32F407串口接收空闲中断+DMA

一、概述 在一些一次性接收大批量数据的引用场合&#xff0c;如果使用接收中断会频繁的进入接收中断影响代码的运行效率。为了解决这个问题可以使用串口的空闲中断DMA实现。 二、应用 在网上招了一些例程在STM32F407的平台上都没有跑通会出现各种异常&#xff0c;主要原因还…...

b站视频爬虫-词云分析

一、设置爬虫程序 # requests 请求b站视频 import jsonimport fake_useragent import requests from lxml import etreeif __name__ == __main__:# UA伪装head = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like …...

如何防止订单二次重复支付?

如何防止订单二次重复支付&#xff1f; 在电商平台和支付系统中&#xff0c;防止订单二次重复支付是一个至关重要的功能。以下是一些常见的策略和技术手段&#xff0c;用于确保订单支付的幂等性和一致性。 目录 唯一订单号订单状态检查数据库事务乐观锁悲观锁支付渠道状态核查…...

LeetCode 24反转链表

单链表反转&#xff1a;详细解析与代码实现 在数据结构的学习过程中&#xff0c;链表是一个非常重要且有趣的部分&#xff0c;而单链表的反转操作更是常考的基础知识点。今天就来和大家详细讲讲如何实现单链表的反转&#xff0c;并通过代码示例来加深理解呀。 题目 给定单链…...

用python的flask写的一个MQTT中转功能,http的方式发送数据和接收数据

需求背景 给一个客户对接人脸识别的设备&#xff0c;最后需要通知服务端进行一些消息推送。 简单例子 # 作者 陈老师 # https://v.iiar.cn import json import paho.mqtt.client as mqtt import requests from flask import Flask, requestapp Flask(__name__)# MQTT配置 mq…...

img引入svg如何修改颜色

方法1&#xff1a;通过css中filter:drop-shadow 首先需要一个容纳图标的父盒子(下方实例中的.svg-img)&#xff0c;通过css造一个图标的‘影子’&#xff08;.svg-color中的drop-shadow&#xff09;&#xff0c;然后设置‘影子’的颜色&#xff0c;再把图标本体移出父盒子&…...

计算机毕业设计PySpark+PyFlink+Hive地震预测系统 地震数据分析可视化 地震爬虫 大数据毕业设计 Hadoop 机器学习 深度学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

手把手教你用NVIDIA TX2串口控制大疆C620电机(USB转CAN模块保姆级教程)

从零实现NVIDIA TX2通过USB-CAN模块精准控制大疆C620电机 硬件连接与基础原理 当我们需要在机器人项目中实现高精度电机控制时&#xff0c;CAN总线通信往往是首选方案。但对于使用NVIDIA Jetson TX2这类开发板的新手来说&#xff0c;可能会遇到两个现实问题&#xff1a;TX2原生…...

MDK5.39编译报错Error:#268?手把手教你切换C99模式解决变量声明问题

MDK5.39编译报错Error:#268的深度解析与C99模式实战指南 当你在Windows 11环境下使用MDK5.39进行ARM嵌入式开发时&#xff0c;突然遇到"Error:#268: declaration may not appear after executable statement in block"这样的编译错误&#xff0c;确实会让人感到困惑。…...

Google Authenticator PHP集成避坑指南:从扫码到验证的完整流程与常见错误解决

Google Authenticator PHP集成深度排障手册&#xff1a;从原理到实战的30个关键细节 当你按照教程一步步完成Google Authenticator的PHP集成&#xff0c;却在最后一步验证失败时&#xff0c;那种挫败感我深有体会。三年前我第一次在金融项目中实现动态口令认证&#xff0c;连续…...

SQL复杂报表如何通过窗口函数优化_减少子查询提升性能

窗口函数可高效替代关联子查询&#xff0c;适用于累计值、移动平均、并列排名等场景&#xff0c;性能提升3–10倍&#xff1b;须注意RANK()与ROW_NUMBER()语义差异、ORDER BY的强制性、ROWS优于RANGE、窗口函数不可用于WHERE/HAVING等关键规则。窗口函数替代关联子查询的典型场…...

Gumbo-parser内存管理终极指南:7个简单步骤避免常见陷阱

Gumbo-parser内存管理终极指南&#xff1a;7个简单步骤避免常见陷阱 【免费下载链接】gumbo-parser An HTML5 parsing library in pure C99 项目地址: https://gitcode.com/gh_mirrors/gu/gumbo-parser Gumbo-parser是一个纯C99编写的HTML5解析库&#xff0c;高效的内存…...

技术面试终极指南:10个反向面试技巧助你问对公司问题

技术面试终极指南&#xff1a;10个反向面试技巧助你问对公司问题 【免费下载链接】reverse-interview Questions to ask the company during your interview 项目地址: https://gitcode.com/gh_mirrors/re/reverse-interview 在技术面试中&#xff0c;反向面试&#xff…...

在FreeRTOS上为Zynq CAN驱动添加任务间通信:一个实用的数据收发框架搭建

在FreeRTOS上为Zynq CAN驱动构建高效任务间通信框架 当我们在Zynq平台上开发基于FreeRTOS的CAN总线应用时&#xff0c;如何安全高效地在中断服务程序(ISR)与任务之间传递数据&#xff0c;是构建稳定系统的关键挑战。本文将深入探讨一个经过实战检验的解决方案——通过消息队列和…...

国外SEO优化公司如何提高网站在搜索引擎的排名_国外SEO优化公司的服务语言支持有哪些

国外SEO优化公司如何提高网站在搜索引擎的排名_国外SEO优化公司的服务语言支持有哪些 在当今全球化的互联网时代&#xff0c;国外SEO优化公司在提升网站在搜索引擎中的排名方面扮演着至关重要的角色。不仅仅是提升网站的曝光率&#xff0c;还能有效地增加网站的访问量和用户转…...

量子密钥分发系统的工程实现(四):后处理流程与FPGA硬件加速剖析

1. QKD后处理流程的核心挑战 量子密钥分发&#xff08;QKD&#xff09;系统的后处理流程就像是一场精密的"密钥提纯"手术。想象一下Alice和Bob通过量子信道传递的原始密钥&#xff0c;就像刚从矿场挖出的原石——含有大量杂质&#xff08;误码&#xff09;、存在形状…...

网络基础面试题:简单谈谈你对CDN的理解?原理+流程图+通俗讲解

网络基础面试题&#xff1a;简单谈谈你对CDN的理解&#xff1f;原理流程图通俗讲解一、前言二、CDN 是什么&#xff1f;&#xff08;一句话核心&#xff09;三、为什么要用 CDN&#xff1f;四、CDN 工作流程图&#xff08;最清晰&#xff09;五、CDN 工作步骤&#xff08;简单 …...