Spring使用WebSocket,发送图片、表情、消息、心跳机制

  • Spring使用WebSocket,发送图片、表情、消息、心跳机制
  • 项目地址:https://gitee.com/BestJohnny/spring-web-socket

IndexController.java


import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.johnny.websocket.WebSocket;

@Controller
public class IndexController {

	@Autowired
	WebSocket webSocket;

	@GetMapping("/ws")
	public String webSocket(HttpServletRequest request, HttpServletResponse response) {
		request.setAttribute("rootPath", request.getContextPath());
		return "webSocket";
	}

	@GetMapping("/index1")
	public String index1() {
		return "index1";
	}

	@GetMapping("/index2")
	public String index2() {
		return "index2";
	}

	@ResponseBody
	@GetMapping("/getUserMenu")
	public String index3() {
		return null;
	}

	@ResponseBody
	@GetMapping("/sendToOne")
	public void sendToOne(@RequestParam("from") String from, @RequestParam("to") String to, @RequestParam("text") String text, @RequestParam("type") String type) throws IOException {
		JSONObject json = new JSONObject();
		json.put("from", from);
		json.put("to", to);
		json.put("text", text);
		json.put("type", type);
		webSocket.sendMessageTo(to, json.toString());
	}

	@ResponseBody
	@GetMapping("/sendToAll")
	public void sendToAll(@RequestParam("from") String from, @RequestParam("to") String to, @RequestParam("text") String text, @RequestParam("type") String type) throws IOException {
		JSONObject json = new JSONObject();
		json.put("from", from);
		json.put("to", to);
		json.put("text", text);
		json.put("type", type);
		webSocket.sendMessageAll(json.toString());
	}
}

WebSocketConfig.java


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

@Configuration
public class WebSocketConfig {

	// 这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket ,如果你使用外置的tomcat就不需要该配置文件
	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}

}

WebSocket.java


import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

/**
 * WebSocketServer接收请求https://blog.csdn.net/qq_42402854/article/details/130948270
 * 
 * @Contacts 171471869@163.com
 * @Author Johnny_阿阳
 * @Date 2023-04-10 10:42:19
 */
@Component
@ServerEndpoint("/webSocket/{userName}")
public class WebSocket {

	public static final Logger log = LogManager.getLogger(WebSocket.class);

	// 心跳机制
	private Timer heartBeatTimer;

	// 是否已关闭
	private boolean isClosed;

	// 用户名称
	private String userName;

	// 会话
	private Session session;

	// 在线人数
	private static AtomicInteger onlineSessionClientCount = new AtomicInteger(0);

	// 存放所有在线的客户端
	private static Map<String, Session> onlineSessionClientMap = new ConcurrentHashMap<>();

	// 以用户的姓名为key,WebSocket为对象保存起来
	private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();

	public WebSocket() {
		this.isClosed = false;
	}

	/**
	 * 连接建立成功调用的方法。由前端<code>new WebSocket</code>触发
	 *
	 * @param userName 每次页面建立连接时传入到服务端的id,比如用户id等。可以自定义。
	 * @param session  与某个客户端的连接会话,需要通过它来给客户端发送消息
	 */
	@OnOpen
	public void onOpen(@PathParam("userName") String userName, Session session) {
		this.userName = userName;
		this.session = session;
		this.session.setMaxIdleTimeout(5 * 60 * 1000);
		this.startHeartBeat();
		String sessionId = session.getId();
		clients.put(userName, this);// 把自己的信息加入到map当中去
		onlineSessionClientMap.put(userName, session);// 将页面的userName和session绑定或者session.getId()与session
		onlineSessionClientCount.incrementAndGet();// 在线数加1

		// 给所有人发送上线通知type=0,from=userName
		Message message1 = new Message(userName, "", "给所有人发送上线通知", "0");
		String msg1 = JSON.toJSONString(message1, true);
		sendTextToAll(msg1);

		// 给自己发送在线人员type=0,from==to,online=[...]
		Set<String> online = onlineSessionClientMap.keySet();
		Message message2 = new Message(userName, userName, "给自己发送在线人员", "0", null, online);
		String msg2 = JSON.toJSONString(message2, true);
		sendTextToOne(userName, msg2);

		// 给自己发送未读消息..
//		Message message3 = new Message(null, userName, msg, "1", "-1", null);
//		String msg3 = JSON.toJSONString(message3, true);
//		sendTextToOne(userName, msg3);

		log.debug("连接建立成功,当前在线数为:{} ==> 开始监听新连接:sessionId = {}, userName = {}", onlineSessionClientCount, sessionId, userName);
	}

	/**
	 * 连接关闭调用的方法。由前端<code>socket.close()</code>触发
	 *
	 * @param userName
	 * @param session
	 */
	@OnClose
	public void onClose(@PathParam("userName") String userName, Session session) {
		this.isClosed = true;
		this.stoptHeartBeat();
		clients.remove(userName);
		onlineSessionClientMap.remove(userName);// 从 Map中移除
		onlineSessionClientCount.decrementAndGet();// 在线数减1

		// 给所有人发送下线通知type=-2,from=userName
		Message message = new Message(userName, null, "给所有人发送下线通知", "-2");
		String msg = JSON.toJSONString(message, true);
		sendTextToAll(msg);

		log.debug("连接关闭成功,当前在线数为:{} ==> 关闭该连接信息:sessionId = {}, userName = {}", onlineSessionClientCount, session.getId(), userName);
	}

	/**
	 * 发生错误调用的方法
	 *
	 * @param session
	 * @param error
	 */
	@OnError
	public void onError(Session session, Throwable error) {
		// 给自己发送失败消息..
		this.stoptHeartBeat();
		String sessionId = session.getId();
		Message message = new Message(null, null, "给自己发送失败消息,sessionId=" + sessionId, "-1", null, null);
		String err = JSON.toJSONString(message, true);
		sendTextToOne(userName, err);// 通知用户
		error.printStackTrace();
	}

	/**
	 * 收到客户端消息后调用的方法。由前端<code>socket.send</code>触发 * 当服务端执行toSession.getAsyncRemote().sendText(xxx)后,前端的socket.onmessage得到监听。
	 *
	 * @param message
	 * @param session
	 */
	@OnMessage
	public void onMessage(String message, Session session) {
		// html界面传递来得数据格式,可以自定义message{"from":"user1","to":"user2","text":"hello","type":"0","online":"[user1,user2...]"}
		JSONObject jsonObject = JSON.parseObject(message);
		String text = jsonObject.getString("text");
		String from = jsonObject.getString("from");
		String to = jsonObject.getString("to");
		log.debug("服务端收到客户端消息 ==> from = {}, to = {}, message = {}", from, to, message);
		if (to == null || to == "" || "".equalsIgnoreCase(to)) {
			sendTextToAll(text);
		} else {
			sendTextToOne(to, text);
		}
	}

	/**
	 * 群发消息<后端>
	 *
	 * @param message 消息
	 */
	public void sendTextToAll(String message) {
		onlineSessionClientMap.forEach((onlineSid, toSession) -> {// 遍历在线map集合
			if (!userName.equalsIgnoreCase(onlineSid)) {
				synchronized (toSession) {
					log.debug("用户向服务器发送广播 ==> message = {}", message);
					toSession.getAsyncRemote().sendText(message);
				}
			}
		});
	}

	/**
	 * 指定用户发送消息<后端>
	 *
	 * @param to
	 * @param message
	 */
	public void sendTextToOne(String to, String message) {
		Session toSession = onlineSessionClientMap.get(to);
		synchronized (toSession) {
			log.debug("用户向{}发送消息 ==> message = {}", to, message);
			toSession.getAsyncRemote().sendText(message);// 异步发送
		}
//		try {
//			toSession.getBasicRemote().sendText(message);// 同步发送
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
	}

	/**
	 * 指定用户发送消息
	 * 
	 * @param message
	 * @param to
	 * @throws IOException
	 */
	public void sendMessageTo(String to, String message) {
		for (WebSocket item : clients.values()) {
			if (item.userName.equals(to)) {
				log.debug("用户向{}发送消息 ==> message = {}", to, message);
				item.session.getAsyncRemote().sendText(message);// 异步发送
				break;
			}
		}
	}

	/**
	 * 群发消息
	 * 
	 * @param message
	 * @throws IOException
	 */
	public void sendMessageAll(String message) {
		for (WebSocket item : clients.values()) {
			if (!item.userName.equalsIgnoreCase(userName)) {
				log.debug("用户向服务器发送广播 ==> message = {}", message);
				item.session.getAsyncRemote().sendText(message);// 异步发送
			}
		}
	}

	/**
	 * 启动心跳定时器
	 */
	private void startHeartBeat() {
		this.heartBeatTimer = new Timer();
		this.heartBeatTimer.schedule(new TimerTask() {
			@Override
			public void run() {
				if (!isClosed) {
					session.getAsyncRemote().sendText("");
					// send();
				}
			}
		}, 60000, 60000);
	}

	/**
	 * 停止心跳定时器
	 */
	private void stoptHeartBeat() {
		if (this.heartBeatTimer != null) {
			this.heartBeatTimer.cancel();
			this.heartBeatTimer = null;
		}
	}

}

Message.java


import java.util.Set;

/**
 * WebSocketMsg发送的消息详情
 * 
 * @Contacts 171471869@163.com
 * @Author Johnny_阿阳
 * @Date 2023-04-10 10:42:19
 */
public class Message {

	public String from;// 发送者name

	public String to;// 接收者name

	public String text;// 发送的文本

	public String type;// 消息类型(-2[下线],-1[失败],0[上线],1[正常消息],2[图片])

	public String status;// 消息状态(失败[-1],未读[0],已读[1]])

	public Set<String> online;// 在线用户

	public Message() {
		super();
	}

	/**
	 * 
	 * @param from
	 * @param to
	 * @param text
	 * @param type 消息类型(-2[下线],-1[失败],0[上线],1[正常消息])
	 */
	public Message(String from, String to, String text, String type) {
		super();
		this.from = from;
		this.to = to;
		this.text = text;
		this.type = type;
	}

	/**
	 * 
	 * @param from
	 * @param to
	 * @param text
	 * @param type   消息类型(-2[下线],-1[失败],0[上线],1[正常消息])
	 * @param status 消息状态([-1]失败,[0]未读,[1]已读])
	 * @param online 在线用户
	 */
	public Message(String from, String to, String text, String type, String status, Set<String> online) {
		super();
		this.from = from;
		this.to = to;
		this.text = text;
		this.type = type;
		this.status = status;
		this.online = online;
	}

	public String getFrom() {
		return from;
	}

	public String getTo() {
		return to;
	}

	public String getText() {
		return text;
	}

	public String getType() {
		return type;
	}

	public String getStatus() {
		return status;
	}

	public Set<String> getOnline() {
		return online;
	}

	public void setFrom(String from) {
		this.from = from;
	}

	public void setTo(String to) {
		this.to = to;
	}

	public void setText(String text) {
		this.text = text;
	}

	public void setType(String type) {
		this.type = type;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public void setOnline(Set<String> online) {
		this.online = online;
	}

	@Override
	public String toString() {
		return "Message [from=" + from + ", to=" + to + ", text=" + text + ", type=" + type + ", status=" + status + ", online=" + online + "]";
	}

}

webSocket.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
  <head>
    <meta charset="UTF-8" />
    <title>Insert title here</title>
    <link rel="stylesheet" th:href="@{/static/FrameworkModule/pear/css/pear.css}">
    <link rel="stylesheet" th:href="@{/static/admin/css/load.css}"/>
    <link rel="stylesheet" th:href="@{/static/admin/css/admin.css}"/>
    <link rel="stylesheet" th:href="@{/static/users/css/index.css}"/>
    <link rel="stylesheet" th:href="@{/static/users/css/sphere.css}">
    <link rel="stylesheet" th:href="@{/static/users/css/chat.css}">
    <link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.mCustomScrollbar.min.css}">
    <link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.emoji.css}">
    
  </head>
  <body>
	<div id="id-layer-wrapper" >
		<div class="layout">
		    <!-- 左侧 -->
	        <div class="layout-left">
	            <ul>
	            	<!-- 头像 -->
	                <li >
	                	<div class="header-img">
		                    <img src="./static/chat/dist/media/img/avatar1.jpg" class="my-header-img rounded-circle" alt="image">
	                	</div>
	                </li>
	                <!-- 用户 -->
	               <li class="left-menu active" title="用户">
	                    <div class="layui-icon layui-icon-user"></div>
	                </li>
	                <!-- 声音 -->
	                <li class="left-menu" title="声音">
	                    <div class="layui-icon layui-icon-notice"></div>
	                </li>
	                <!-- 菜单 -->
	                <li class="left-menu" title="菜单">
	                    <div class="layui-icon layui-icon-app"></div>
	                </li>
	                <!-- 退出 -->
	                <li class="left-menu" title="退出">
	                    <div class="layui-icon layui-icon-logout"></div>
	                </li>
	            </ul>
	        </div>

	        <!-- 中间 -->
	        <div class="layout-middle">
	            <!-- 在线列表 -->
	            <div id="online" class="online-group">
                    <ul class="list-group" id="online_list" ></ul>
	            </div>
	        </div>
		
	        <!-- 右侧 -->
	        <div class="layout-right">
	        	<!-- 右侧头部 -->
	            <div class="chat-header">
	                <div id="chat-header" title="群聊"  class="chat-header-user">
	                	<div class="header-img">	                	
	                        <img src="./static/chat/dist/media/img/chat.png" class="rounded-circle" alt="image">
	                	</div>

	                    <div>
	                        <h5>群聊</h5>
	                    </div>
	                </div>
	            </div>
	            <!-- 右侧中间 -->
	            <div class="chat-body" id="messages">
	                <div class="messages active" title="群聊" ></div>
	            </div>
	            <!-- 右侧底部 -->
	            <div class="chat-footer">
	                <div>
	                    <textarea id="send-Chat-Msg" class="form-control" placeholder="发送消息..."></textarea>
	                    <div class="form-buttons">
	                        <button title="表情" id="send-Chat-face" onclick="chatOperation(this)" class=" layui-icon layui-icon-face-smile-fine" ></button>
	                        <button title="语言" id="send-Chat-mike" onclick="chatOperation(this)" class=" layui-icon layui-icon-mike" ></button>
	                        <button title="发送" id="send-Chat-release" onclick="chatOperation(this)" class=" layui-icon layui-icon-release" ></button>
	                        <button title="相机" id="send-Chat-camera" onclick="chatOperation(this)" class=" layui-icon layui-icon-camera" ></button>
	                        <button title="文件" id="send-Chat-file" onclick="chatOperation(this)" class=" layui-icon layui-icon-file" ><input id="upload-file" type="file" name="file"/></button>
	                        <button title="定位" id="send-Chat-location" onclick="chatOperation(this)" class=" layui-icon layui-icon-location" ></button>
	                    </div>
	                </div>
	            </div>
	        </div>
		</div>
	</div>

<script th:src="@{/static/FrameworkModule/jquery.min.js}"></script>
<script th:src="@{/static/FrameworkModule/layui2_8/layui.js}"></script>
<script th:src="@{/static/FrameworkModule/pear/pear.js}"></script>
<script th:src="@{/static/users/js/myutil.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/highlight.pack.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mousewheel-3.0.6.min.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mCustomScrollbar.min.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.emoji.min.js}"></script>

<script type="text/javascript">
	
	var ws = null;
	var music = null;
	var sendFrom = null;
	let rootPath="[[${rootPath}]]";
    var singleMsg = './static/users/music/singleMsg.mp3';
    var groupMsg = './static/users/music/groupMsg.mp3';
    var online = './static/users/music/online.mp3';
	
	layui.use(['jquery','layer','element','admin','form','popup','util'], function () {
		let $ = layui.jquery;
		let util = layui.util;
		let form = layui.form;
		let layer = layui.layer;
		let admin = layui.admin;
		let element = layui.element;
		let popup = layui.popup;
		var num1 = Math.floor(Math.random()*9999+1);
		var nickName = "user"+num1;
		
		if ("WebSocket" in window) {//localhost:8080/springWebSocket自己改
			  ws = new WebSocket("ws:localhost:8080/springWebSocket/webSocket/"+nickName);
			} else if ("MozWebSocket" in window) {
			  ws = new MozWebSocket("ws:localhost:8080/springWebSocket/webSocket/"+nickName);
			} else {
			  //ws = new SockJS(wsUrl + nickName);
			  layer.msg('Not support websocket', {icon: 2, time: 2000});
			}
			//新建连接
			ws.onopen = function (event) {
			  var num = Math.floor(Math.random()*9+1);
			  var img = './static/chat/dist/media/img/avatar' + num + '.jpg';
			  $('.my-header-img').attr('src',img);
			  var html = '<li title="群聊" οnclick="sendTo(this)" class="list-group-item" data-navigation-target="online-msg">';
	    		html += '<div><figure class="avatar"><img src="./static/chat/dist/media/img/chat.png" class="rounded-circle" alt="image"></figure></div>';
	    		html += '<div class="users-list-body"><div><h5 >群聊</h5><p title="群聊">空闲...</p></div></div></li>';
	    		document.getElementById('online_list').innerHTML = html;
			};
			//监听消息
			ws.onmessage = function (event) {
			  var data = JSON.parse(event.data);
			  if (data.type == 1 || data.type == 2) { //图片消息
				  doReceiveMsg(data);
				  //layer.msg('正常消息', {icon: 1, time: 1000});
			  } else if (data.type == 0  && data.from == data.to && data.to.length > 0) { //自己获取在线人员
				  onlineUser(data.online);
				  //layer.msg('自己获取在线人员', {icon: 1, time: 10000});						  
			  } else if (data.type == 0  && data.from != data.to && data.to.length == 0) { //通知所有人xx上线了
				  addOnlineUser(data.from);
				  //layer.msg('通知所有人xx上线了', {icon: 1, time: 10000});						  
			  } else if (data.type == -1) { //错误消息
				  layer.msg('错误消息', {icon: 1, time: 1000});						  						  
			  } else if (data.type == -2) { //下线消息
				  delOnlineUser(data.from);
				  //layer.msg('下线消息', {icon: 1, time: 1000});						  						  						  
			  }
			};
			//发送失败
			ws.onerror = function () {
			  layer.msg('发送失败', {icon: 2, time: 10000});
			};
			//断开连接
			ws.onclose = function (e) {
				var data = '断开连接: [错误码:'+e.code+'; 断开原因:'+e.reason+'; 正常关闭:'+e.wasClean+']';
				//console.log(data);
				layer.msg(data, {icon: 2, time: 1000});
			};
		

		//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
		window.onbeforeunload = function(){
			close();
		}
		
		//关闭浏览器
		window.close = function () {
			ws.close();
		};
		
		//聊天框按钮
		window.chatOperation = function (obj) {
			var title = $(obj).attr("title");
			if ('发送'==title) {
				doSendText();
				var mmmm = $("#messages .messages .message-item .message-content");
				matchEmoji(mmmm);
			}else if ('文件'==title) {
				var myfile = $("#upload-file");
				myfile[0].addEventListener('change',function (e) {
					var fileList = document.getElementById("upload-file").files;
					//console.log(fileList);
					if ('image/jpeg'!=fileList[0].type) {	
						layer.msg('紧支持发送图片', {icon: 2, time: 1000});	
					}else {
						doSendImages();											
					}
				},{once:true});
			}else if ('表情'==title) {
				loadEmoji();
			}
		}
		
		<!-- 在线户名 -->
		window.onlineUser = function (data){
			for (var i = 0; i < data.length; i++) {
				var num = Math.floor(Math.random()*9+1);
				var img = './static/chat/dist/media/img/avatar' + num + '.jpg';
				var name = (data[i]==nickName) ? '记忆助手(我)' : data[i];
				img = (data[i]==nickName) ? ($('.my-header-img').attr('src')) : img;
				var html = '<li title="' + name + '" οnclick="sendTo(this)" class="list-group-item" data-navigation-target="online-msg">';
		    		html += '<div><figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure></div>';
		    		html += '<div class="users-list-body"><div><h5 >'+name+'</h5><p title="' + data[i] + '">空闲...</p></div></div></li>';
		    	$('#online_list').append(html);
		    	var html = '<div class="messages" title="' + name + '" ></div>';
			    $('#messages').append(html);
			}
		}
		
		<!-- 增加在线户名 -->
		window.addOnlineUser = function (name){
			var num = Math.floor(Math.random()*9+1);			
			var img = './static/chat/dist/media/img/avatar' + num + '.jpg';
			var html = '<li title="' + name + '" οnclick="sendTo(this)" class="list-group-item" data-navigation-target="online-msg">';
	    		html += '<div><figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure></div>';
	    		html += '<div class="users-list-body"><div><h5 >'+name+'</h5><p title="' + name + '">空闲...</p></div></div></li>';
	    	$('#online_list').append(html);
	    	var html = '<div class="messages" title="' + name + '" ></div>';
	    	$('#messages').append(html);
		}
		
		<!-- 移除下线户名 -->
		window.delOnlineUser = function (name){
			var li = $('li[title="' +  name +  '"]');
			var div = $('.messages[title="' +  name +  '"]');
			li.addClass('logout');
			$('.logout .users-list-body div p').html('用户已离开');
			setTimeout(() => {
				li.remove();
				div.remove();
				var src = $('li[title="群聊"] .avatar img').attr('src');
				$('#chat-header').find('img').attr("src",src);
				$('#chat-header').attr("title","群聊");
				$('#chat-header').find('h5').text("群聊");
				$('#messages .messages[title="群聊"]').addClass('active');
			}, 60*1000);
		}
		
		<!-- 选择发送人 -->
		window.sendTo = function (obj){
			var title = $(obj).attr("title");
			var src = $(obj).find('img').attr("src");
			$('#chat-header').attr("title",title);
			$('#chat-header').find('img').attr("src",src);
			$('#chat-header').find('h5').text(title);
			$('#messages .messages').removeClass('active');
			$('#messages .messages[title="'+title+'"]').addClass('active');
			$(obj).removeClass('new-msg');
		}
		
		<!-- 发送文字消息 -->
		window.doSendText = function () {
		    var sentTime = new Date().format('yyyy-MM-dd hh:mm:ss');
			var title = $('#chat-header').attr("title");
			var msg = $('#send-Chat-Msg').val();
			var img = $('.my-header-img').attr('src');
		    var html = '<div class="message-item outgoing-message"><div class="message-avatar">';
		    	html += '<figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure>';
		    	html += '<div><h5>'+ nickName +'</h5><div class="time">'+sentTime+'</div></div>';
		    	html += '</div><div class="message-content">'+msg+'</div></div>';
			if ("群聊" == title && msg.length > 0) {//群聊
				$('.messages[title="'+title+'"] ').append(html);
				var data = {"from":nickName,"to":"群聊","text":msg,"type":"1"};
				var url = rootPath+"/sendToAll";
				ajaxGetData(url, data);	
			} else if (msg.length > 0){				
				$('.messages[title="'+title+'"] ').append(html);
				var data = {"from":nickName,"to":title,"text":msg,"type":"1"};
				var url = rootPath+"/sendToOne";
				ajaxGetData(url, data);
			}
			$('#send-Chat-Msg').val("");
		}
		
		<!-- 发送图片消息 -->
	     window.doSendImages = function() {
	        var fileList = document.getElementById("upload-file").files;
	        var sentTime = new Date().format('yyyy-MM-dd hh:mm:ss');
			var title = $('#chat-header').attr("title");
			var img = $('.my-header-img').attr('src');
	        if(fileList.length > 0){
	            var fileReader = new FileReader();
	            fileReader.readAsDataURL(fileList[0]);
	            fileReader.onload = function (e) {
	            	var msg = e.target.result;
	            	var sendImg = '<img class="chat-img" src="' + msg + '"';
	            	var html = '<div class="message-item outgoing-message"><div class="message-avatar">';
			    		html += '<figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure>';
			    		html += '<div><h5>'+ nickName +'</h5><div class="time">'+sentTime+'</div></div>';
			    		html += '</div><div class="message-content">'+sendImg+'</div></div>';
	            	if ("群聊" == title) {//群聊
	    				var data = {"from":nickName,"to":"群聊","text":msg,"type":"2"};
	    				var url = rootPath+"/sendToAll";
	    				ajaxGetData(url, data);	
	            		$('.messages[title="'+title+'"] ').append(html);
	    			} else {
	    				var data = {"from":nickName,"to":title,"text":msg,"type":"2"};
	    				var url = rootPath+"/sendToOne";
	    				ajaxGetData(url, data);
	    				$('.messages[title="'+title+'"] ').append(html);
	    			}
	            }
	        }
	    }

		<!-- 接收文字/图片消息 -->
		window.doReceiveMsg = function (data){
			var from = data.from;
			var to = data.to;
			var text = data.text;
			var type = data.type;
			var title = $('#chat-header').attr("title");
		    var receiveTime = new Date().format('yyyy-MM-dd hh:mm:ss');;
		    var img = $('li[title="' +  from +  '"]').find('img').attr('src');
		    var receiveImg = '<img class="chat-img" src="' + text + '"';
		    text = type==2 ? receiveImg : text;// 图片?/文字?
		    var html = '<div class="message-item"><div class="message-avatar">';
		    	html += '<figure class="avatar"><img src="'+img+'" class="rounded-circle" alt="image"></figure>';
		    	html += '<div><h5>'+from+'</h5><div class="time">'+receiveTime+'</div></div>';
		    	html += '</div><div class="message-content">'+text+'</div></div>';
				music = new Audio();
		    if (nickName != from  && to !="群聊") {
		    	music.src = singleMsg;
				$('p[title="' +  from +  '"]').html(text);
				$('li[title="' +  from +  '"]').addClass('new-msg');
			    $('.messages[title="' + from + '"] ').append(html);
			}else if (nickName != from  && to =="群聊") {
				music.src = groupMsg;
				$('p[title="群聊"]').html(text);
				$('li[title="群聊"]').addClass('new-msg');
				$('.messages[title="群聊"]').append(html);
			}
			var mmmm = $("#messages .messages .message-item .message-content");
			matchEmoji(mmmm);
		    var playPromise = music.play();
		    if (playPromise) {
                playPromise.then(() => {
                    // 音频的播放需要耗时
                    setTimeout(() => {
                        // 后续操作
                    }, music.duration * 1000); // music.duration 为音频的时长单位为秒
                }).catch((e) => {
                    // 音频加载失败
                });
            }
		}

		//菜单.unbind('click')
		$(document).on("click", '.left-menu ', function () {
			$(".left-menu").removeClass("active");
			var active = $(this).addClass("active");;
			var title = $(this).attr("title");
		});
	
	})


</script>
	
  </body>
</html>

js、css懒得删,全部搞进来了

<script th:src="@{/static/FrameworkModule/jquery.min.js}"></script>
<script th:src="@{/static/FrameworkModule/layui2_8/layui.js}"></script>
<script th:src="@{/static/FrameworkModule/pear/pear.js}"></script>
<script th:src="@{/static/users/js/myutil.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/highlight.pack.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mousewheel-3.0.6.min.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.mCustomScrollbar.min.js}"></script>
<script th:src="@{/static/FrameworkModule/emoji/dist/js/jquery.emoji.min.js}"></script>
    <link rel="stylesheet" th:href="@{/static/FrameworkModule/pear/css/pear.css}">
    <link rel="stylesheet" th:href="@{/static/admin/css/load.css}"/>
    <link rel="stylesheet" th:href="@{/static/admin/css/admin.css}"/>
    <link rel="stylesheet" th:href="@{/static/users/css/index.css}"/>
    <link rel="stylesheet" th:href="@{/static/users/css/sphere.css}">
    <link rel="stylesheet" th:href="@{/static/users/css/chat.css}">
    <link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.mCustomScrollbar.min.css}">
    <link rel="stylesheet" th:href="@{/static/FrameworkModule/emoji/dist/css/jquery.emoji.css}">

效果展示:
发送
接收
表情

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值