当前位置: 首页 > 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领…...

java_网络服务相关_gateway_nacos_feign区别联系

1. spring-cloud-starter-gateway 作用&#xff1a;作为微服务架构的网关&#xff0c;统一入口&#xff0c;处理所有外部请求。 核心能力&#xff1a; 路由转发&#xff08;基于路径、服务名等&#xff09;过滤器&#xff08;鉴权、限流、日志、Header 处理&#xff09;支持负…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...