uniapp:全局消息是推送,实现app在线更新,WebSocket,apk上传

全局消息是推送,实现app在线更新,WebSocket

    • 1.在main.js中定义全局的WebSocket
    • 2.java后端建立和发送WebSocket
    • 3.通知所有用户更新

背景
开发人员开发后app后打包成.apk文件,上传后通知厂区在线用户更新app。
那么没在线的怎么办?因为我们在上一篇博客中写了,在app打开的时候回去校验是否需要更新了,所以已经完成了闭环。

即时通讯首先想到的就是WebSocket

  • 1.我们定义全局的WebSocket
  • 2.在全局监听,当监听到指定消息的时候弹窗更新,下载逻辑也就是下载最新的apk,在上一篇博客写了,点击下方链接。
    uniapp:实现手机端APP登录强制更新,从本地服务器下载新的apk更新,并使用WebSocket,实时强制在线用户更新

但是有一个问题,就是手持机少还可以,要是多的话,几百台连接还不是不太合适?,所以用登录的时候检测更新是最好的
使用post调用测试

在这里插入图片描述

@GetMapping("/sendAllUser")public void sendAllUser(){webSocketServer.sendAllMessage("updateApp");}

1.在main.js中定义全局的WebSocket

import App from './App'
import Vue from 'vue'
import uView from 'uni_modules/uview-ui'
import debounce from '@/utils/debounce'Vue.use(uView)
Vue.config.productionTip = false;
App.mpType = 'app'
Vue.prototype.$debounce = debounce;
import store from '@/store';const app = new Vue({store,...App
})// 创建全局的事件总线
Vue.prototype.$eventBus = new Vue();// 创建全局WebSocket连接
Vue.prototype.$socket = uni.connectSocket({url: 'ws://127.0.0.1:8080/webSocket',complete: (res) => {console.log('WebSocket connection completed:', res);},
});// 全局监听WebSocket消息
Vue.prototype.$socket.onMessage((res) => {console.log('Received WebSocket message:', res);if(res.data == 'updateApp'){uni.navigateTo({url: '/pages/index/upgrade'})}// 将消息传递给事件总线,以便在整个应用中进行处理// Vue.prototype.$eventBus.$emit('socketMessage', res);
});// 引入请求封装,将app参数传递到配置中
require('@/config/http.interceptor.js')(app)app.$mount()

当接受到updateApp的消息的时候,打开更新弹窗,详情见链接:

2.java后端建立和发送WebSocket

下面java后端建立和发送WebSocket 消息的地方,若依自带websocket连接代码,咕咕在其基础上做了一些修改,添加了部分方法

package com.qygx.framework.websocket;import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Semaphore;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.stereotype.Component;/*** websocket 消息处理* * @author ruoyi*/
//@ConditionalOnClass(value = WebSocketConfig.class)
@ServerEndpoint("/webSocket")
@Component
public class WebSocketServer
{/*** WebSocketServer 日志控制器*/private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServer.class);/*** 默认最多允许同时在线人数100*/public static int socketMaxOnlineCount = 100;private static Semaphore socketSemaphore = new Semaphore(socketMaxOnlineCount);private static CopyOnWriteArraySet<WebSocketServer> webSockets =new CopyOnWriteArraySet<>();//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 用户ID*/private String userId;/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session) throws Exception{this.session = session;this.userId = userId;webSockets.add(this);boolean semaphoreFlag = false;// 尝试获取信号量semaphoreFlag = SemaphoreUtils.tryAcquire(socketSemaphore);if (!semaphoreFlag){// 未获取到信号量LOGGER.error("\n 当前在线人数超过限制数- {}", socketMaxOnlineCount);WebSocketUsers.sendMessageToUserByText(session, "当前在线人数超过限制数:" + socketMaxOnlineCount);session.close();}else{// 添加用户WebSocketUsers.put(session.getId(), session);LOGGER.info("\n 建立连接 - {}", session);LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size());WebSocketUsers.sendMessageToUserByText(session, "连接成功");}}/*** 连接关闭时处理*/@OnClosepublic void onClose(Session session){webSockets.remove(this);LOGGER.info("\n 关闭连接 - {}", session);// 移除用户WebSocketUsers.remove(session.getId());// 获取到信号量则需释放SemaphoreUtils.release(socketSemaphore);}/*** 抛出异常时处理*/@OnErrorpublic void onError(Session session, Throwable exception) throws Exception{if (session.isOpen()){// 关闭连接session.close();}String sessionId = session.getId();LOGGER.info("\n 连接异常 - {}", sessionId);LOGGER.info("\n 异常信息 - {}", exception);// 移出用户WebSocketUsers.remove(sessionId);// 获取到信号量则需释放SemaphoreUtils.release(socketSemaphore);}/*** 服务器接收到客户端消息时调用的方法*/@OnMessagepublic void onMessage(String message, Session session){String msg = message.replace("你", "我").replace("吗", "");WebSocketUsers.sendMessageToUserByText(session, msg);}// 此为广播消息public void sendAllMessage(String message) {for(WebSocketServer webSocket : webSockets) {try {if(webSocket.session.isOpen()) {webSocket.session.getAsyncRemote().sendText(message);}} catch (Exception e) {e.printStackTrace();}}}
}

3.通知所有用户更新

咕咕这里是在用户上传完,提交表单成功后发送消息的,大家可自定义。

/*** 新增app版本*/@PreAuthorize("@ss.hasPermi('system:appversion:add')")@Log(title = "app版本", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody SysAppVersion sysAppVersion){SysAppVersion newestVersion = sysAppVersionService.getNewestVersion();sysAppVersionService.insertSysAppVersion(sysAppVersion);if(newestVersion != null && newestVersion.getAppVersionCode() < sysAppVersion.getAppVersionCode()){webSocketServer.sendAllMessage("updateApp");}return toAjax(sysAppVersionService.insertSysAppVersion(sysAppVersion));}

下面的是,文件上传的代码,大家可以随意参考,基于若依的一些修改,本来若依也是开源的,所以这个也就不做删减了。

package com.qygx.mes.app.controller;import com.qygx.common.config.RuoYiConfig;
import com.qygx.common.core.domain.AjaxResult;
import com.qygx.common.utils.DateUtils;
import com.qygx.common.utils.StringUtils;
import com.qygx.common.utils.file.FileUploadUtils;
import com.qygx.common.utils.file.FileUtils;
import com.qygx.common.utils.file.MimeTypeUtils;
import com.qygx.common.utils.uuid.Seq;
import com.qygx.framework.config.ServerConfig;
import com.qygx.framework.websocket.WebSocketServer;
import com.qygx.mes.csm.domain.SysAppVersion;
import com.qygx.mes.csm.service.ISysAppVersionService;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Objects;@RestController
@RequestMapping("/app")
public class AppController {@Autowiredprivate WebSocketServer webSocketServer;@Autowiredprivate ServerConfig serverConfig;@Autowiredprivate ISysAppVersionService sysAppVersionService;@GetMapping("/download")public void download(String path, HttpServletResponse response) {try {//拿到最新的版本//http://127.0.0.1:8080/profile/upload/2023/12/28/__UNI__E832695__20231227183906_20231228110314A001.apkSysAppVersion newestVersion = sysAppVersionService.getNewestVersion();File file = new File("d://app//"+newestVersion.getFileName());String filename = file.getName();String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();FileInputStream fileInputStream = new FileInputStream(file);InputStream fis = new BufferedInputStream(fileInputStream);byte[] buffer = new byte[fis.available()];fis.read(buffer);fis.close();response.reset();// 设置response的Headerresponse.setCharacterEncoding("UTF-8");//Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存//attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));// 告知浏览器文件的大小response.addHeader("Content-Length", "" + file.length());OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());response.setContentType("application/octet-stream");outputStream.write(buffer);outputStream.flush();} catch (IOException ex) {ex.printStackTrace();}}/*** 通用上传请求(单个)*/@PostMapping("/upload")public AjaxResult uploadFile(MultipartFile file) throws Exception{try{String newName = extractFilename(file);String absPath = "D:\\app\\" + newName;file.transferTo(Paths.get(absPath));// 上传文件路径// 上传并返回新文件名称AjaxResult ajax = AjaxResult.success();ajax.put("url", absPath);ajax.put("fileName", "fileName");ajax.put("newFileName", newName);ajax.put("originalFilename", file.getOriginalFilename());return ajax;}catch (Exception e){return AjaxResult.error(e.getMessage());}}private String extractFilename(MultipartFile file){return StringUtils.format("{}_{}.{}",FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file));}/*** 获取文件名的后缀** @param file 表单文件* @return 后缀名*/public static final String getExtension(MultipartFile file){String extension = FilenameUtils.getExtension(file.getOriginalFilename());if (StringUtils.isEmpty(extension)){extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));}return extension;}@GetMapping("/getNewestVersion")public AjaxResult getNewestVersion(){SysAppVersion newestVersion = sysAppVersionService.getNewestVersion();HashMap<String, Object> map = new HashMap<>();if(newestVersion != null ){map.put("newVersionName", newestVersion.getAppVersion());map.put("newVersionCode", newestVersion.getAppVersionCode());}else {map.put("newVersionName", "v");map.put("newVersionCode", 0);}return AjaxResult.success(map);}@GetMapping("/sendAllUser")public void sendAllUser(){webSocketServer.sendAllMessage("updateApp");}}

先上传文件嘛,然后将路径什么的,返回给表单提交,简单的。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

如何文件从电脑传到iPhone,这里提供几个方法

本文介绍了如何使用Finder应用程序、iTunes for Windows、iCloud和谷歌照片将照片从Mac或PC传输到iPhone。 如何将照片从Mac传输到iPhone 如果你有一台Mac电脑&#xff0c;里面装满了你想转移到iPhone的照片&#xff0c;这是一件非常简单的事情。只需遵循以下步骤&#xff1a…

参数归一化-实现时间格式化

文章目录 需求分析具体实现完整源码 不知道大家有没有尝试封装过一个时间格式化的函数啊&#xff0c;在之前我封装的时候&#xff0c;开始是觉得手到擒来&#xff0c;但是实践之后发现写非常的shi啊&#xff0c;大量的分支判断&#xff0c;哪怕是映射起到的作用也只是稍微好一点…

第二十一章Java网络通信

网络通信这一章 基本分为三个部分 网络基础概念和TCP,UDP这三个部分主要如下&#xff1a; 计算机网络实现了堕胎计算机间的互联&#xff0c;使得它们彼此之间能够进行数据交流。网络应用程序就是再已连接的不同计算机上运行的程序&#xff0c;这些程序借助于网络协议&#x…

Java线上问题排查思路

1、Java 服务常见问题 Java 服务的线上问题从系统表象来看大致可分成两大类: 系统环境异常、业务服务异常。 系统环境异常&#xff1a;主要从CPU、内存、磁盘、网络四个方面考虑。比如&#xff1a;CPU 占用率过高、CPU 上下文切换频率次数较高、系统可用内存长期处于较低值、…

第二证券:普通人怎么选个股?

普通人怎么选个股 1、成果 成果是推动个股上涨的内在动力&#xff0c;即成果好的个股能推动个股持续上涨&#xff0c;成果差的个股会导致个股持续跌落&#xff0c;因而&#xff0c;投资者应该选择成果较好的个股。 2、资金 资金是影响股价涨跌的一重要因素&#xff0c;当资…

使用electron属性实现保存图片并获取图片的磁盘路径

在普通的网页开发中&#xff0c;JavaScript由于安全性的考虑&#xff0c;通常是无法直接获取到客户端的磁盘路径的。浏览器出于隐私和安全原因对此类信息进行了限制。 在浏览器环境下&#xff0c;JavaScript主要通过Web APIs来与浏览器进行交互&#xff0c;而这些API通常受到浏…

代码随想录-刷题第四十一天

343. 整数拆分 题目链接&#xff1a;343. 整数拆分 思路&#xff1a;动态规划五步曲 dp[i]&#xff1a;拆分数字i&#xff0c;可以得到的最大乘积为dp[i]。 递推公式&#xff1a;dp[i] max(dp[i], max((i - j) * j, dp[i - j] * j)) 从1遍历j&#xff0c;有两种渠道得到dp[…

媲美保时捷:小米汽车正式亮相| 一周 IT资讯

1、对标保时捷、特斯拉&#xff01;小米汽车首款产品发布 12月28日&#xff0c;万众瞩目之下&#xff0c;小米汽车首场技术发布会终于揭开神秘面纱&#xff0c;来自全国各地的米粉齐聚北京&#xff0c;共同见证了小米汽车的技术特色及优势。 在本次发布会上&#xff0c;小米汽…

数据库进阶教学——读写分离(Mycat1.6+Ubuntu22.04主+Win10从)

目录 1、概述 2、环境准备 3、读写分离实验 3.1、安装jdk 3.2、安装Mycat 3.3、配置Mycat 3.3.1、配置schema.xml ​​​​3.3.2、配置server.xml 3.4、修改主从机远程登陆权限 3.4.1、主机 3.4.2、从机 3.5、启动Mycat 3.6、登录Mycat 3.7、验证 1、概述 读写分…

【HarmonyOS开发】案例-记账本开发

OpenHarmony最近一段时间&#xff0c;简直火的一塌糊度&#xff0c;学习OpenHarmony相关的技术栈也有一段时间了&#xff0c;做个记账本小应用&#xff0c;将所学知识点融合记录一下。 1、记账本涉及知识点 基础组件&#xff08;Button、Select、Text、Span、Divider、Image&am…

TikTok真题第8天 | 418.屏幕可显示句子的数量、395.至少有K个重复字符的最长子串、1010.总持续时间可以被60整除的歌曲对

418.屏幕可显示句子的数量 题目链接&#xff1a;418.sentence-screen-fitting 解法&#xff1a; 这道题&#xff0c;看题解都很难看懂&#xff0c;哪怕看出点门道了&#xff0c;也很难用自己的话解释出来。 有几点必须清楚&#xff1a; &#xff08;1&#xff09;将字符串…

10. Opencv检测并截取图中二维码

1. 说明 在二维码扫描功能开发中,使用相机扫描图片时,往往图片中的信息比较多样,可能会造成二维码检测失败的问题。一种提高检测精度的方式就是把二维码在图片中单独抠出来,去除其它冗余信息,然后再去识别这张提取出来的二维码。本篇博客记录采用的一种实现二维码位置检测…

计算机网络——应用层与网络安全(六)

前言&#xff1a; 前几章我们已经对TCP/IP协议的下四层已经有了一个简单的认识与了解&#xff0c;下面让我们对它的最顶层&#xff0c;应用层进行一个简单的学习与认识&#xff0c;由于计算机网络多样的连接形式、不均匀的终端分布&#xff0c;以及网络的开放性和互联性等特征&…

L1-069:胎压监测

题目描述 小轿车中有一个系统随时监测四个车轮的胎压&#xff0c;如果四轮胎压不是很平衡&#xff0c;则可能对行车造成严重的影响。 让我们把四个车轮 —— 左前轮、右前轮、右后轮、左后轮 —— 顺次编号为 1、2、3、4。本题就请你编写一个监测程序&#xff0c;随时监测四轮的…

GaussDB数据库中的同义词SYNONYM

目录 一、前言 二、GasussDB数据库中的Synonym 1、Synonym的概念 2、语法介绍 3、Synonym的用途 三、Synonym在GaussDB数据库中是如何使用的 1、表的同义词使用&#xff08;示例&#xff09; 2、视图的同义词使用&#xff08;示例&#xff09; 3、函数的同义词使用&am…

分布式IO在工业自动化中的应用

传统的自动化产线及物流系统主要是利用PLC来处理数据&#xff0c;并将这些数据保存在PC当中。但是随着互联网技术的迅速发展&#xff0c;越来越多的系统集成商利用分布式IO模块&#xff0c;实现从控制器到自动化最底层之间的IO通信。 分布式IO在工业自动化中的应用 分布式IO是用…

Vue3+ElementPlus: 给点击按钮添加触发提示

一、需求 在Vue3项目中&#xff0c;有一个下载按钮&#xff0c;当鼠标悬浮在按钮上面时&#xff0c;会出现文字提示用户可以点击按钮进行数据的下载技术栈 Vue3 ElementPlusTooltip组件 ElementPlus中的Tooltip组件 &#xff0c;可用于展示鼠标 hover 时的提示信息 二、实现…

【SD】IP-Adapter 进阶 - 垫图 【1】

目录 关于SD1.5的画风迁移 修改动作-方法一&#xff1a;提示词 修改动作-方法二&#xff1a;openpose 关于SD1.5的画风迁移 1.5测试模型&#xff1a;flat2DAnimerge_v30_2.safetensors [b2c93e7a89] 测试图&#xff1a; 文生图&#xff1a;best quality,masterpiece, co…

GPT-5、开源、更强的ChatGPT!

年终岁尾&#xff0c;正值圣诞节热闹气氛的OpenAI写下了2024年的发展清单。 OpenAI联合创始人兼首席执行官Sam Altman在社交平台公布&#xff0c;AGI&#xff08;稍晚一些&#xff09;、GPT-5、更好的语音模型、更高的费率限制&#xff1b; 更好的GPTs&#xff1b;更好的推理…

weblogic未授权命令执行漏洞(CVE-2020-14882)

漏洞描述&#xff1a; 未经身份验证的远程攻击者可能通过构造特殊的 HTTP GET请求&#xff0c;利用该漏洞在受影响的 weblogic Server 上执行任意代码。 复现过程&#xff1a; 1.访问ip&#xff1a;port/console 2.poc构造 #!/usr/bin/env python3 # -*- coding: utf-8 -*-…