个人博客——使用websocket实现评论实时展示

上篇文章完成了博客评论区的样式和评论回复功能,接下来基于websocket实现评论的实时展示。
需求: 浏览博客的用户,可以看到评论区的实时评论
思路: 用户打开博客详情后,向后端发送打开的博客id和用户token,后端将这些信息保存到map中用于发送消息和关闭连接。

后端

  1. 引入依赖,pom.xml中添加
        <!--websocket-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
  1. 添加websocket文件夹,添加以下文件夹和文件
    在这里插入图片描述
    因为一篇文章可以同时被多个用户浏览,所以使用嵌套的map存储被浏览的文章id和真正浏览文章的用户token和对应会话,嵌套map结构如图所示,发送消息只发送给浏览同一篇文章的用户
    在这里插入图片描述

WebSocketServer.java

/**
 * 注意在websocket通信中只能传string
 */
@Component
@ServerEndpoint("/socket/{passageId}/{userToken}")
public class WebSocketServer {
//    存储被浏览的文章id和正在浏览文章的用户token和对应会话
    public static final Map<String, Map<String,Session>> sessionMap = new ConcurrentHashMap<>();
    public static CommentService commentService;

//    建立连接
    /***
     * 1.第一层map存储被浏览的文章id和浏览的用户及会话
     * 2.第二层map存储用户token及会话
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("passageId") String passageId,@PathParam("userToken") String userToken) {

        if(sessionMap.containsKey(passageId)){
            sessionMap.get(passageId).put(userToken,session);
        }else {
            Map<String,Session> map = new HashMap<>();
            map.put(userToken,session);
            sessionMap.put(passageId,map);
        }

    }

    //关闭连接
    /**
     * 1.把登出的用户从sessionMap中剃除
     * 2.发送给所有人当前登录人员信息
     */
    @OnClose
    public void onClose(@PathParam("passageId") String passageId,@PathParam("userToken") String userToken) {
       //用户退出文章的浏览,则将该用户及其会话移除
        Map<String,Session> passageMap = sessionMap.get(passageId);
        passageMap.remove(userToken);
        //没有用户浏览该文章,则将文章id从map中移除
        if(passageMap.isEmpty()){
            sessionMap.remove(passageId);
        }


    }

    /**
     * 接收处理客户端发来的数据
     */
    @OnMessage
    public void onMessage(String message) {
//        解析消息为java对象
        Comment msg = JSON.parseObject(message, Comment.class);
        if(msg != null&& msg.getCommentContent() != null &&!msg.getCommentContent().isEmpty()){
            //将评论内容存入数据库
            msg.setCommentTime(new Date());
            commentService.insertSelective(msg);
            //将评论内容发送给其他正在浏览该文章的用户
            sendAllMessage(msg);
        }else{
            System.out.println("评论内容为空");

        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }


//   服务端发送消息给客户端
    private void sendAllMessage(Comment message) {

        try {
            String passageId = Integer.toString(message.getPassageId()) ;
            //根据评论发送文章的id,将该评论发送给正在浏览该文章的用户
            Map<String,Session> passageMap = sessionMap.get(passageId);
            for (Session session : passageMap.values()) {
                session.getBasicRemote().sendText(JSON.toJSONString(message));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

CorsConfig .java

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路由
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                .allowedOriginPatterns("*")
                // 是否允许证书(cookies)
                .allowCredentials(true)
                // 设置允许的方法
                .allowedMethods("*")
                // 跨域允许时间
                .maxAge(3600);
    }

}

WebSocketConfig.java

@Configuration
public class WebSocketConfig {
    /**
     * 使用springboot内置tomcat需要该bean,打war包则注释掉该bean
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }

    //因为websocket中不能直接使用@Autowired注入Service,添加下面配置 在socket引入Service
    @Autowired
    public void socketUserService(CommentService commentService){
        WebSocketServer.commentService = commentService;
    }


}

前端

  1. 打开文章详情时初始化websocket
onMounted(()=>{
  getPassageDetail();
  getPassageComment();
  if(userMsg.userToken){
    init();
  }
})
//websocket
const info = reactive(new CommentClass());
let socket:any;
const init = () =>{
        // 如果sessionStorage中没有用户信息,则跳转登录页面   
        if (typeof (WebSocket) == "undefined") {
          console.log("您的浏览器不支持WebSocket");
        } else {
          console.log("您的浏览器支持WebSocket");
          let socketUrl = "ws://localhost:8080/socket/" + passageId+"/"+userMsg.userToken;
          if (socket != null) {
            socket.close();
            socket = null;
          }
          // 开启一个websocket服务
          socket = new WebSocket(socketUrl);
          //打开事件
          socket.onopen = function () {
            console.log("websocket已打开");
          };
          //  浏览器端收消息,获得从服务端发送过来的文本消息
          socket.onmessage = function (msg:any) {
            console.log("收到数据====" , msg.data)
            let data = JSON.parse(msg.data)
            if(!data.rootParentId){
              commentList.value.push(data);
            }else{
            //给被评论的地方添加数据
              for(let i=0;i<commentList.value.length;i++){
                 if(data.rootParentId == commentList.value[i].commentId){
                  if(commentList.value[i].child != null){
                    commentList.value[i].child[data.commentId] = data;
                  }else{
                    commentList.value[i].child={};
                    commentList.value[i].child[data.commentId] = data;
                  }
                  break;
                 }

              }
              
            }
          };
          //关闭事件
          socket.onclose = function () {
            console.log("websocket已关闭");
          };
          //发生了错误事件
          socket.onerror = function () {
            console.log("websocket发生了错误");
          }
        }
      }

  1. 发布评论,分为在文章下发表评论和在评论下回复评论两种情况,都通过websocket向后端发送评论
//发布评论
const publishComment = (rootParentId?:number,parentId?:number)=>{
  console.log( rootParentId == undefined && parentId == undefined)
  //在文章下发表评论
  if(rootParentId == undefined && parentId == undefined){
     console.log(commentPublish.commentContent)
      if(commentPublish.commentContent == '' ||commentPublish.commentContent ==  undefined){
        ElMessage.warning('评论不能为空');
        return
      }
       commentPublish.userId =userMsg.userId;
       commentPublish.userName = userMsg.userName;
       commentPublish.passageId = passageId;
      if (typeof (WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
          } else {
            console.log("您的浏览器支持WebSocket");
            socket.send(JSON.stringify(commentPublish));
            Object.assign(commentPublish,commentEnpty);

          }
  }
  //在评论下回复评论
  else{
    if(commentReply.commentContent == '' ||commentReply.commentContent ==  undefined){
        ElMessage.warning('评论不能为空');
        return
      }
    if(commentReply.commentContent == ''){
        ElMessage.warning('评论不能为空');
        return
      }
      if(rootParentId != undefined){
      commentReply.rootParentId = rootParentId;
      }
      if(parentId != undefined){
        commentReply.parentId = parentId;
      }
       commentReply.userId =userMsg.userId;
       commentReply.userName = userMsg.userName;
       commentReply.passageId = passageId;
      if (typeof (WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
          } else {
            console.log("您的浏览器支持WebSocket");
            socket.send(JSON.stringify(commentReply));
            Object.assign(commentReply,commentEnpty);
            replyInput.value = -1;
          }
  }
  
}

至此,评论区实时展示就完成了,实现效果如下
请添加图片描述
本文参考

springboot+vue+websocket配置参考
websocket中使用service参考

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值