首页 > 其他分享 >uniapp:全局消息是推送,实现app在线更新,WebSocket,apk上传

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

时间:2024-01-04 11:04:15浏览次数:38  
标签:uniapp WebSocket app session file qygx import public



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

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


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

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

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

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

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

@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的消息的时候,打开更新弹窗

下面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;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public 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, "连接成功");
        }
    }

    /**
     * 连接关闭时处理
     */
    @OnClose
    public void onClose(Session session)
    {
        webSockets.remove(this);
        LOGGER.info("\n 关闭连接 - {}", session);
        // 移除用户
        WebSocketUsers.remove(session.getId());
        // 获取到信号量则需释放
        SemaphoreUtils.release(socketSemaphore);
    }

    /**
     * 抛出异常时处理
     */
    @OnError
    public void one rror(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);
    }

    /**
     * 服务器接收到客户端消息时调用的方法
     */
    @OnMessage
    public 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)
    @PostMapping
    public 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 {
    @Autowired
    private WebSocketServer webSocketServer;
    @Autowired
    private ServerConfig serverConfig;
    @Autowired
    private 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.apk
            SysAppVersion 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的Header
            response.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");
    }

}

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

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


标签:uniapp,WebSocket,app,session,file,qygx,import,public
From: https://blog.51cto.com/u_15601494/9097024

相关文章

  • uniapp:实现手机端APP登录强制更新,从本地服务器下载新的apk更新,并使用WebSocket,实时强
    实现登录即更新,或实时监听更新本文介绍的是在App打开启动的时候调用更新,点击下方链接,查看使用WebSocket实现实时通知在线用户更新。uniapp:全局消息是推送,实现app在线更新,WebSocket,apk上传:登录更新流程1.在app每次启动的时候请求java后端,2.后端接口获取最新的版本:3.打开更新页面4......
  • Apple Logic Pro 10.7 (Logic Pro 10.7 + MainStage 3.5 (Universal))
    作者:gc,主页:www.sysin.orgLogicPro本领先声夺人,创意一鸣惊人。使用实时循环乐段,以全新方式进行音乐创作和即兴演奏。借助采样器和快速采样器将声音转化为乐器。通过步进音序器来快速制作鼓点节拍和旋律模式。利用LogicRemote在iPad或iPhone上掌控乐曲的创作。LogicProMai......
  • 远程安卓盒子N1安装APP-远程查看机顶盒屏幕画面等
    title:远程安卓盒子N1安装IPTVAPP-远程查看机顶盒屏幕画面等tags:[N1,Android,安卓,iptv,家宽,远程控制,机顶盒,frps,frpc,frp]新版原文:https://www.carlzeng.top/search?q=远程安卓盒子N1安装APP-远程查看机顶盒屏幕画面等版权声明:本博客所有文章除特别声明外,均采用BY......
  • “尝试输入密码的次数太多 还原 Apple Watch 并重新配对。” 的正确解法
    “尝试输入密码的次数太多还原AppleWatch并重新配对。”的正确解法作者主页:www.sysin.org说明:本文适用于watchOS7和iOS14,不排除未来产品更新致使操作方法有所变更。今日一不小心也中招,如图。开始搜索一通,排在前面的全部是错误解法。实在无语…正确官方解法如下:https://su......
  • eqwalizer WhatsApp 开源的erlang类型检查工具
    可能大家都听说过WhatsApp对于erlang使用是比较多的,WhatsApp的IM系统就是基于erlang开发的,同时WhatsApp也开源了一些erlang周边的工具eqwalizer是一个类型检查的,可以与rebar以及非rebar项目集成参考使用基于rebar的项目下载工具https://github.com/WhatsApp/eqwalizer/......
  • Xcode 15 beta 6 (15A5219j) - Apple 平台 IDE
    Xcode15beta6(15A5219j)-Apple平台IDEIDEforiOS/iPadOS/macOS/watchOS/tvOS/visonOS作者主页:sysin.orgvisonOS支持已更新。Xcode15使您能够为所有Apple平台开发、测试和分发应用程序。通过增强的代码完成、交互式预览和实时动画,更快地编写和设计您的应用程序。使用......
  • Apple iWork(Pages、Numbers、Keynote)11.0 发布
    苹果今天将其专为iOS和macOS设备设计的iWork应用套件更新为版本11,引入了许多新功能和改进功能。Pages文稿。文档高手,精美不言而喻。进一步了解Pages文稿>Numbers表格。精妙的电子表格,联手做。进一步了解Numbers表格>Keynote讲演。令人难忘的出色提案,轻松打造。进......
  • DAPP智能合约交易系统定制开发
     DAPP智能合约交易系统开发、dapp合约交易所开发,dapp合约交易系统软件开发智能合约系统根据事件描述信息中包含的触发条件,当触发条件满足时,从智能合约自动发出预设的数据资源,以及包括触发条件的事件;整个智能合约系统的核心就在于智能合约以事务和事件的方式经过智能合约模块的处理......
  • Spring MVC 源码分析 - WebApplicationContext 容器的初始化
    随着 SpringBoot 和 SpringCloud 在许多中大型企业中被普及,可能你已经忘记当年经典的Servlet+SpringMVC的组合,是否还记得那个 web.xml 配置文件。在开始本文之前,请先抛开 SpringBoot 到一旁,回到从前,一起来看看Servlet是怎么和SpringMVC集成,怎么来初始化Sprin......
  • 软件开发app制作公司排名前十
    以下是2024年软件开发app制作公司排名前十:华盛恒辉科技有限公司:是一家专注于高端软件定制开发服务和高端建设的服务机构,致力于为企业提供全面、系统的开发制作方案。在开发、建设到运营推广领域拥有丰富经验,在教育,医疗,APP,管理,商城,人工智能,ERP,系统二次开发,CRM等领域有很多成......