WebSocket实现HTML+SpringBoot聊天功能,小程序+SpringBoot聊天功能

news2024/11/16 23:41:46

目录

一、认识WebSocket

二、HTML实现聊天

三、微信小程序实现聊天


一、认识WebSocket

1.首先博主在初学Java时自我感觉走了很多弯路,因为以前见识短,在接触聊天功能时根本就没能想到有WebSocket这个聊天框架,就只能用底层的UDP或TCP实现聊天功能,及其繁琐。

1.在入门Java后的朋友学到网络编程会知道UDP和TCP两个知识点,没错WebSocket是一种在单个TCP连接上进行全双工通信的协议。基于TCP协议的一个框架,TCP知识点比较多,具体咱们就不多说了,直接实践怎么使用吧。

二、HTML实现聊天

首先我先贴出完整代码,然后解释

1.html代码,这里我就不单独写js文件了(这个html实现的是一对一聊天,还有一对多,多对多群聊)

<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
<script src="https://cdn.jsdelivr.net/sockjs/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>

<script>
    var stompClient = null;

    function connect() {
	var content = $("#message").val();
        var sender = 'tbb';
        var socket = new SockJS('http://127.0.0.1:8080/chat');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (frame) {
            console.log('Connected: ' + frame);
            stompClient.subscribe('/user/tbb/message', function (message) {
                showMessage(JSON.parse(message.body));
            });
        });
    }

    function disconnect() {
        if (stompClient !== null) {
            stompClient.disconnect();
        }
        console.log("Disconnected");
    }

    function sendMessage() {
        var content = $("#message").val();
        var sender = $("#sender").val();
        stompClient.send("/app/chat/send/to/user", {}, JSON.stringify({
            content: content,
            sender: sender,
	    unionId: sender,
        }));
    }

    function showMessage(message) {
        $("#chat").append("<div>" + message.sender + ": " + message.content + "</div>");
    }
</script>

<div>
    <input type="text" id="sender" placeholder="Enter your name">
    <input type="text" id="message" placeholder="Type a message...">
    <button onclick="sendMessage()">Send</button>
    <button onclick="connect()">Connect</button>
    <button onclick="disconnect()">Disconnect</button>
</div>

<div id="chat">123</div>

 2.SpringBoot完整代码

(1)WebSocketConfig.java配置文件(关键文件)

package com.example.mengchuangyuan.common.chat.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/user","/agent","/topic"); // 定义消息代理,客户端订阅的地址前缀
        config.setApplicationDestinationPrefixes("/app"); // 定义客户端发送消息的地址前缀
        config.setUserDestinationPrefix("/user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").setAllowedOriginPatterns("*").withSockJS(); // 定义WebSocket端点,客户端连接的地址
    }
}

(2)ChatController.java控制层 

package com.example.mengchuangyuan.common.chat.controller;

import com.example.mengchuangyuan.common.chat.entry.ChatMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.stereotype.Controller;

@Slf4j
@Controller
public class ChatController {
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    /**
     * 发消息(群发)
     * @param chatMessage 获取用户消息
     * @return 返回要回复的消息
     */
    //第一种方式
    @MessageMapping("/chat/send") // 定义消息映射路径
    @SendTo("/agent/public") // 发送消息到指定的代理路径
    public ChatMessage sendMessage(ChatMessage chatMessage) {
        log.info(chatMessage.toString());
        return chatMessage;
    }

    //方法二
//    @MessageMapping("/agent/send")
//    public void getAgentInfo (@Payload ChatMessage chatMessage) {
//        System.out.println("发送群发消息");
//        // 使用api进行推送
//        simpMessagingTemplate.convertAndSend("/agent/public2", chatMessage);
//    }

    /**
     * 发送给自己?
     * @param chatMessage
     * @return
     */
    @MessageMapping("/agent/send/user")
// 这里的路径必须还是以广播的前缀为前缀,否则无法接收
    @SendToUser("/agent/info")
    public ChatMessage sendUserMessage(ChatMessage chatMessage) {
        log.info(chatMessage.toString());
        return chatMessage;
    }

    /**
     * 发送给指定用户
     * @param chatMessage
     * @return
     */
    @MessageMapping("/chat/send/to/user")
// 这里的路径必须还是以广播的前缀为前缀,否则无法接收
    public void sendToUserMessage(ChatMessage chatMessage) {
        log.info("发送给指定用户:"+chatMessage.toString());
        simpMessagingTemplate.convertAndSendToUser(chatMessage.getUnionId(),"/message",chatMessage);
    }
}
package com.example.mengchuangyuan.common.chat.entry;

import lombok.Data;

@Data
public class ChatMessage {
    private String unionId;
    private String content;
    private String sender; 
}

以上就是完整代码

3.接下来我来简单解释一下,因为一对一聊天比其他相对绕一点,所以博主就解释它就好了,且看下面四段被截取的代码

 function connect() {
	var content = $("#message").val();//发送的消息内容
        var sender = 'tbb';//接收的人
        var socket = new SockJS('http://127.0.0.1:8080/chat');//连接后端socket的地址
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (frame) {
            console.log('Connected: ' + frame);
            //这里我写死了,接收来自tbb这个人的消息,'/user/tbb/message'可以改成'/user/'+sender+'/message'
            stompClient.subscribe('/user/tbb/message', function (message) { 
                showMessage(JSON.parse(message.body));
            });
        });
    }

上面的代码可以称之为连接服务器并且实时监听tbb给后端发来的消息 

package com.example.mengchuangyuan.common.chat.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/user","/agent","/topic"); // 定义消息代理,客户端订阅的地址前缀
        config.setApplicationDestinationPrefixes("/app"); // 定义客户端发送消息的地址前缀
        config.setUserDestinationPrefix("/user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").setAllowedOriginPatterns("*").withSockJS(); // 定义WebSocket端点,客户端连接的地址
    }
}

上面两段是对应的,在html的http://127.0.0.1:8080/chat中chat对应上面Java代码的chat,必须一致,要不然连不上,/user/tbb/message路由中user对应上面Java代码的user,必须一致,要不然发消息时对方收不到消息(博主以前踩过这个坑),而这个tbb则是接收发消息的人的消息



    function sendMessage() { //发消息给对方
        var content = $("#message").val();
        var sender = $("#sender").val();
        stompClient.send("/app/chat/send/to/user", {}, JSON.stringify({ //发消息的后端的路由
            content: content,//消息内容
            sender: sender,//消息姓名
	    unionId: sender,//消息id,这里我把消息姓名也作为id
        }));
    }
 /**
     * 发送给指定用户
     * @param chatMessage
     * @return
     */
    @MessageMapping("/chat/send/to/user")
// 这里的路径必须还是以广播的前缀为前缀,否则无法接收
    public void sendToUserMessage(ChatMessage chatMessage) {
        log.info("发送给指定用户:"+chatMessage.toString());
        
//chatMessage.getUnionId()是发送给某人的id,/message对应四段中第一段的/user/tbb/message中的message
simpMessagingTemplate.convertAndSendToUser(chatMessage.getUnionId(),"/message",chatMessage);
    }

 最后两段是对应的,用于给对方发消息。

三、微信小程序实现聊天

首先,这是博主自己摸索了很久出来的一套小程序聊天体系。

1.聊天数据结构及框架

涉及到了redis缓存,因此需要下载redis的依赖包

聊天数据结构如下(自我感觉存在一定缺陷,懒得改进了):

整体储存结构:

聊天界面结构:{openid1+openid2:{linkType:[info1,info2]}},

如图聊天界面:

聊天列表结构:{linkType:[openid1+openid2,openid3+openid4]}

如图聊天列表:


info的结构:{mid:"",type:"",linkType:"",formUser:fromUser,toUser:toUser,message:"",date:"",nowDate:""}
fromUser和toUser的结构:{openid:"",phone:"",name:"",headImg:""}
openid1和openid2为fromUser和toUser的openid

linkType:属于哪个板块聊天(比如相亲聊天板块或者外卖或商城聊天板块)


type:作用于获取redis缓存的聊天记录和聊天心跳检测(备注:因为获取历史聊天记录和心跳检测是以聊天方式向后端发起请求,因此我用type的聊天要区分是用户发起聊天还是其他请求。由前端自动发动聊天请求,获取历史聊天记录,由前端发起聊天请求检测心跳,检测心跳的目的是为了确保聊天过程不掉线)


date:聊天的时间段(备注:可以设置5分钟显示一个时间段聊天的时间,比如微信隔5分钟后再发一条信息上面会显示时间,这里我设置的date就充当这个角色)


nowDate:每一句话的时间,主要用于计算当前时间是否与上一句聊天记录的时间是否间隔5分钟,如果间隔5分钟那么上面的date记录该时间,如果间隔不到5分钟,则date设置为空。
整体结构上:两个人的openid连接作为获取他们之间所有功能板块历史聊天记录的内容的键(key)。以linkType作为键(key)获取某个功能板块的所有历史聊天记录,这里聊天记录用集合来储存保证了聊天记录顺序。

2.Java SpringBoot代码

(1)MiniWebSocketConfig.java文件
package com.example.mengchuangyuan.common.chat.mini.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class MiniWebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

(2)MiniWebSocketController.java文件

package com.example.mengchuangyuan.common.chat.mini.controller;

import com.example.mengchuangyuan.common.redis.tool.SufferVariable;
import com.example.mengchuangyuan.common.tool.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.*;

@Slf4j
@RestController
@RequestMapping("/mini/socket")
public class MiniWebSocketController {
    @Value("${upload.messageChatUrl}")
    private String messageChatUrl;

    @Value("${upload.messageChatPath}")
    private String messageChatPath;

    private final List<Map<String,Object>> mapList = new ArrayList<>();

    @GetMapping("/get/openid/history")
    private Result getOpenidHistory(@RequestParam("openid") String openid,@RequestParam("linkType") String linkType){
//        log.info(openid);
        List<String> openids = (List<String>) SufferVariable.openidKeys.get(linkType);
//        if (keyMap==null){
//            return Result.error();
//        }
//        List<String> openids = (List<String>) keyMap.get(openid);
//        log.info("所以openid"+String.valueOf(openids));
        if (openids==null){
            return Result.error();
        }
        mapList.clear();
        for (String openid1: openids) {
            if (openid1.contains(openid)) {
//            if (SufferVariable.messageMap.get(openid1)!=null){
                mapList.add((Map<String, Object>) SufferVariable.messageMap.get(openid1));
//                log.info("聊天记录缓存"+openid);
//                log.info(String.valueOf(SufferVariable.messageMap.get(openid1)));

//            }
            }
        }
        return Result.success(mapList);
    }


//上传聊天图片
    @PostMapping("/img/upload")
    public Result imgUpDown(@RequestParam("file") MultipartFile file,
                            @RequestParam("filename") String filename) throws IOException {
        System.out.println(filename);
        File file1 = new File(messageChatPath,filename);
        if(!file1.exists()) {
            if(!file1.mkdirs()){ //创建目录
                return Result.error();
            }
        }
        //获取文件名
        String fileName = file.getOriginalFilename();
        //获取文件后缀名。也可以在这里添加判断语句,规定特定格式的图片才能上传,否则拒绝保存。
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //为了避免发生图片替换,这里使用了文件名重新生成
        fileName = UUID.randomUUID()+suffixName;

        file.transferTo(new File(file1,fileName));

        return Result.success(messageChatUrl+filename+"/"+fileName);
    }
}

(3)WebSocketEndPoint.java文件

package com.example.mengchuangyuan.common.chat.mini.mapper;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.mengchuangyuan.common.redis.mapper.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

//对外公布的一个后端站点
//ws://localhost:8080/websocket/用户id
@ServerEndpoint(value = "/websocket/{userId}")
@Component
@Slf4j
public class WebSocketEndPoint {
    //与某个客户端的连接会话,需要他来给客户端发送数据
    private Session session;

    @Autowired
    private SessionPool sessionPool;
//    @Autowired
//    private RedisUtils redisUtils;
    private static WebSocketEndPoint webSocketEndPoint;
    //初始化 ②
    @PostConstruct
    public void init() {
        webSocketEndPoint = this;
        webSocketEndPoint.sessionPool = this.sessionPool;
    }

    //连接建立成功调用的方法
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        //把会话加入连接池中
        //userId通过用户传入,session是系统自动产生
        SessionPool.sessions.put(userId, session);

        //TODO 可以添加日志操作
    }

    //关闭会话的时候
    @OnClose
    public void onClose(Session session) throws IOException {
        webSocketEndPoint.sessionPool.close(session.getId());
        session.close();
    }

    //接收客户端的消息后调用的方法,在这里可以进行各种业务逻辑的操作
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println(message);
//        log.info("redisUtils:"+redisUtils);
        //心跳检测
        if (message.equalsIgnoreCase("ping")) {
            try {
                Map<String, Object> params = new HashMap<>();
                params.put("type", "pong");
                session.getBasicRemote().sendText(JSON.toJSONString(params));
            } catch (Exception e) {
                e.printStackTrace();
            }
            return;
        }
        //将Json字符串转为键值对
//        HashMap params = JSON.parseObject(message, HashMap.class);
        JSONObject params =  JSON.parseObject(message);
        webSocketEndPoint.sessionPool.sendMessage(params);

        //这里的业务逻辑仅仅是把收到的消息返回给前端
//        SessionPool.sendMessage(message);
    }
}

(4)SessionPool.java文件

package com.example.mengchuangyuan.common.chat.mini.mapper;

import com.alibaba.fastjson.JSON;
import com.example.mengchuangyuan.common.redis.mapper.RedisUtils;
import com.example.mengchuangyuan.common.redis.tool.SufferVariable;
import com.example.mengchuangyuan.common.tool.DateYMDms;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import javax.websocket.Session;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Slf4j
public class SessionPool {
    @Autowired
    private RedisUtils redisUtils;

    //key-value : userId - 会话(系统创建)
    public static Map<String, Session> sessions = new ConcurrentHashMap<>();//避免多线程问题

    public void close(String sessionId) {
        //sessionId是在session中添加了一个标识,准确定位某条session
        for (String userId : SessionPool.sessions.keySet()) {
            Session session = SessionPool.sessions.get(userId);
            if (session.getId().equals(sessionId)) {
                sessions.remove(userId);
                break;
            }
        }
    }

    public void sendMessage(String userId, String message) {
        sessions.get(userId).getAsyncRemote().sendText(message);
    }


    //消息的群发,业务逻辑的群发
    public void sendMessage(String message) {
//        redisUtils.cacheValue("chatMessage","String.valueOf(SufferVariable.messageMap)");
        for (String sessionId : SessionPool.sessions.keySet()) {
            SessionPool.sessions.get(sessionId).getAsyncRemote().sendText(message);
        }
    }

    //点对点的消息推送
    public void sendMessage(Map<String, Object> params) {
        log.info("消息内容:"+String.valueOf(params));
        long mid = System.currentTimeMillis();
        Map<String,Object> formUser = (Map<String, Object>) params.get("formUser");
        Map<String,Object> toUser = (Map<String, Object>) params.get("toUser");

        String userId = formUser.get("openid").toString();
        String toUserId = toUser.get("openid").toString();
//        String msg = params.get("message").toString();
        String type  = params.get("type").toString();
        String linkType = params.get("linkType").toString();
        //获取用户session
        Session session = sessions.get(toUserId);

        Map<String,Object> keyMap;
        List<String> setOpenid;
        Map<String,Object> map;
        List<Object> list;
        String uid = userId+toUserId;
        params.put("mid",mid);

        if (SufferVariable.messageMap.get(userId+toUserId)==null&&SufferVariable.messageMap.get(toUserId+userId)==null){
//            messageMap.put(userId+toUserId,userId+toUserId);
            map = new HashMap<>();
            list = new ArrayList<>();

            map.put(linkType,list);
            SufferVariable.messageMap.put(uid,map);

//            list.add(params);

        }else {
            if (SufferVariable.messageMap.get(userId+toUserId)!=null){
                uid = userId+toUserId;

            }else {
               uid = toUserId+userId;
            }
            map = (Map<String, Object>) SufferVariable.messageMap.get(uid);
            list = (List<Object>) map.get(linkType);

        }

        //获取历史记录
        if (type.equalsIgnoreCase("history")){
            if (sessions.get(userId) != null) {
                Map<String, Object> map2 = new HashMap<>();
//                map = (Map<String, Object>) SufferVariable.messageMap.get(uid);
                map2.put("type", "isHistory");
                map2.put("message",list);
                sessions.get(userId).getAsyncRemote().sendText(JSON.toJSONString(map2));
            }
            return;
        }
        String nowDate = DateYMDms.getUtilDate();
        if (list.size()!=0) {
            Map<String, Object> lastMap = (Map<String, Object>) list.get(list.size() - 1);
            String date = DateYMDms.getYMDms(5, lastMap.get("nowDate").toString());
            params.put("date", date);
        }else {
            params.put("date", nowDate);
        }
//        if (SufferVariable.openidKeys.get(linkType)==null){
//            keyMap = new HashMap<>();
//        }else {
//            keyMap = (Map<String, Object>) SufferVariable.openidKeys.get(linkType);
//        }
//        if (keyMap.get(userId)==null){
//            setOpenid = new ArrayList<>();
//        }else {
//            log.info(String.valueOf(SufferVariable.openidKeys));
//            setOpenid = (List<String>) keyMap.get(userId);
//        }
        if (SufferVariable.openidKeys.get(linkType) == null){
            setOpenid = new ArrayList<>();
        }else {
            setOpenid = (List<String>) SufferVariable.openidKeys.get(linkType);
        }
        setOpenid.add(uid);
        Set<String> set = new LinkedHashSet<>(setOpenid);
        setOpenid.clear();
        setOpenid.addAll(set);
//        keyMap.put(userId,setOpenid);
        SufferVariable.openidKeys.put(linkType,setOpenid);

        params.put("nowDate",nowDate);
        list.add(params);
        map.put(linkType,list);
        SufferVariable.messageMap.put(uid,map);
        redisUtils.cacheValue("chatMessage",SufferVariable.messageMap);
        redisUtils.cacheValue("chatOpenidKeys",SufferVariable.openidKeys);

        if (session != null) {
            log.info(uid+":"+String.valueOf(SufferVariable.messageMap.get(uid)));
            session.getAsyncRemote().sendText(JSON.toJSONString(params));
        }
//        log.info(String.valueOf(messageMap));
//        params.remove("formUserId");
        //session不为空的情况下进行点对点推送
    }
}

以上小程序相关代码存在一些缺陷,并且未完整,

需要小程序及SpringBoot完整代码的朋友可以私信博主,

好了本次分享就到此结束。

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

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

相关文章

基于Spring Boot的饮食分享平台设计与实现

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

vue项目打包部署到服务器并使用cdn加速

配置 vue.config.js文件 const isProd process.env.NODE_ENV production module.exports {// 其他配置chainWebpack: config > {// 生产环境下使用CDNif (isProd) {config.plugin(html).tap(args > {args[0].cdn assetsCDNreturn args})}},// 生产环境下替换路径为c…

第十二回 急先锋东郭争功 青面兽北京斗武-FreeBSD/Linux SSH配置和常用软件

周谨和杨志第一轮比枪&#xff0c;为了安全&#xff0c;将枪尖去掉&#xff0c;包上柔软的毡片&#xff0c;再蘸满石灰。两人打了四五十个回合&#xff0c;只见周谨身上斑斑点点&#xff0c;约有三五十处&#xff0c;而杨志身上只有左肩牌下一点白。 周谨不服又来比射箭&#x…

如何在云服务上通过docker部署服务?

如何在云服务上通过docker部署服务&#xff1f; 一、在云服务器上安装Docker1、查看云服务器的OS信息2、[安装Docker并使用&#xff08;Linux&#xff09;](https://help.aliyun.com/zh/ecs/use-cases/deploy-and-use-docker-on-alibaba-cloud-linux-2-instances) 二、通过dock…

互斥锁/读写锁(Linux)

一、互斥锁 临界资源概念&#xff1a; 不能同时访问的资源&#xff0c;比如写文件&#xff0c;只能由一个线程写&#xff0c;同时写会写乱。 比如外设打印机&#xff0c;打印的时候只能由一个程序使用。 外设基本上都是不能共享的资源。 生活中比如卫生间&#xff0c;同一…

视频尺寸魔方:分层遮掩3D扩散模型在视频尺寸延展的应用

▐ 摘要 视频延展(Video Outpainting)是对视频的边界进行扩展的任务。与图像延展不同&#xff0c;视频延展需要考虑到填充区域的时序一致性&#xff0c;这使得问题更具挑战性。在本文中&#xff0c;我们介绍了一个新颖的基于扩散模型的视频尺寸延展方法——分层遮掩3D扩散模型(…

Apollo Cyber RT:引领实时操作系统在自动驾驶领域的创新

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《linux深造日志》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

Linux的常见指令和基本操作演绎【复习篇章一】

文章目录 前言下载安装 XShellXShell 下的复制粘贴热键操作01.ls指令tree 02.cd指令03.touch指令04.mkdir指令&#xff08;重要&#xff09;&#xff1a;05.rmdir指令 && rm 指令&#xff08;重要&#xff09;06.组合07.man指令&#xff08;重要&#xff09;&#xff1…

【lodash.js】非常好用高性能的 JavaScript 实用工具库,防抖,深克隆,排序等

前言&#xff1a;lodash是一款前端必须要知道的js库&#xff0c;它里面提供了许多常用的功能和实用的工具函数 基本上我参与的项目中都有lodash&#xff0c;只能说lodash太强大了&#xff0c;lodash.js 提供了超过 300 个实用的工具函数&#xff0c;涵盖了很多常见的编程任务 l…

群辉NAS的远程访问

群辉NAS是私有云存储&#xff0c;局域网访问很容易【详见&#xff1a;网上邻居访问设置、其它设备的访问设置】&#xff0c;远程访问相对复杂&#xff0c;涉及很多关键因素&#xff0c;现将过程记录如下&#xff1a; 目录 1、互联网接入 2、绑定MAC与IP地址 3、路由器开启5…

HTML5与App封装技术将网站一键打包成App

HTML5&#xff1a;跨平台的利器HTML5作为一种先进的网页标记语言&#xff0c;其最大的优势在于跨平台性。开发者仅需编写一次代码&#xff0c;即可在各种操作系统和设备上运行&#xff0c;无需为每个平台单独开发App。这种“编写一次&#xff0c;运行处处”的模式&#xff0c;大…

选择海外云手机需要考虑什么?

随着跨境电商行业的蓬勃发展&#xff0c;企业们纷纷寻找提升平台流量和广告投放效果的方法&#xff0c;这已成为业界的当务之急。传统的宣传模式在国内受到直播和链接带货等新兴方式的冲击&#xff0c;而在国外&#xff0c;类似的趋势也在悄然兴起&#xff0c;呈现出广阔的发展…

OOM 内存溢出与线上内存实时监控

单应用可用的最大内存 dalvik.vm.heapstartsize&#xff0c;它表示堆分配的初始大小。 APP启动的初始分配内存 dalvik.vm.heapgrowthlimit&#xff0c;它表示单个进程内存限定值。App最大内存限制 dalvik.vm.heapsize&#xff0c;单个进程可用的最大内存。开启largeHeap"t…

力扣646. 最长数对链

动态规划 思路&#xff1a; 思路与 力扣354. 俄罗斯套娃信封问题 类似将序列进行排序&#xff0c;然后假设 dp[i] 为第 i 个元素的最长数对链个数&#xff1b;则其状态转移方程&#xff1a; 第 i 个元素之前的某一个元素&#xff08;假设是下标是 j&#xff09;&#xff0c;如…

VS2022联合Qt5开发学习11(QT5.12.3联合VTK在VS2022上开发医学图像项目5——qvtkWidget上显示STL三维图像并取点)

这篇博文是接着这个系列前面的博文&#xff0c;来讲如何实现医学图像三视图同步视图。我想到的一个思路是用Scrollbar来控制切面的改变&#xff0c;还有一个想法是在三维图像上取点&#xff0c;然后以这个点为切面中心更新三维视图。这篇博文主要介绍的就是第二个想法的三维图像…

软件功能测试如何确定测试需求?CMA、CNAS软件测试报告获取

软件功能测试是为了验证软件的功能是否按照设计要求正常工作的过程&#xff0c;可以确保软件的质量&#xff0c;提高用户体验&#xff0c;也是保证软件安全和可靠性的重要一环。我们需要从多个角度对软件的各个功能模块进行测试&#xff0c;确保每个功能都能正常运行&#xff0…

蓝桥杯备战——5.动态数码管扫描

1.分析原理图 经查阅说明书得知数码管为共阳极&#xff0c;共阳端口接到了U8,而段码接到了U7。 如果需要选中U8,我们只需要将P250;P261;P271; 如果需要选中U7,我们只需要将P251;P261;P271; 2.代码示例 void Delay1ms() //12.000MHz {unsigned char data i, j;i 12;j 169;…

Elasticsearch内核解析 - 数据模型篇

Elasticsearch内核解析 - 数据模型篇 - 知乎 Elasticsearch是一个实时的分布式搜索和分析引擎&#xff0c;它可以帮助我们用很快的速度去处理大规模数据&#xff0c;可以用于全文检索、结构化检索、推荐、分析以及统计聚合等多种场景。 Elasticsearch是一个建立在全文搜索引擎…

【运维】Ubuntu18.04系统docker方式安装ElasticSearch和kibana

前言 最近需要搭建一套测试环境&#xff0c;用到了ElasticSearch( 简称es)搜索引擎&#xff0c;安装过程有些曲折&#xff0c;记录下来作为经验。 正文 环境 Ubuntu18.04 操作系统Docker Server Version: 20.10.7ElasticSearch Version: 8.5.3Kibana Version: 8.5.3 说明 E…

【C++】STL和vector容器

STL和vector容器 基本概念六大组件容器算法迭代器容器算法迭代器 vector容器基本概念vector构造函数赋值vector的容量和大小vector插入与删除vector存取数据函数原型 vector互换容器vector预留空间vector容器嵌套容器 基本概念 长久以来&#xff0c;软件届一直希望建立一种可重…