如果了解了ajax,请坐电梯
前言
私信(聊天)功能,有一点是不能忽略的,那就是消息的实时发送与实时接收,否则的话就直接退化成为留言板功能了。
之前我在论坛中有问到这个实现的方法,我考虑了websocket 和 ajax 长轮询这两个方法,很多人也有说websocket比较消耗资源。最终在权衡服务器和客户端的情况下,使用了ajax方法(websocket不会写)
ajax的能干什么?
使用Ajax技术网页应用能够快速地将增量更新呈现在用户界面上,而不需要重载(刷新)整个页面,这使得程序能够更快地回应用户的操作。(节选自百度百科)
简单的来说,使用ajax,页面不刷新就可以验证用户名是否存在。在页面不刷新的情况下向服务器端发送一个请求,在得到服务器的响应后,把结果重新传到页面上并进行显示。
哪里用到了ajax?
尝试在百度输入一个候选词,然后下方会弹出推荐,使用键盘上下进行选择。在你还没有按下回车的时候,页面的内容就已经变了,而如果是刷新的话,文本框中的内容就应该已经没有了,但是使用ajax就可以有这样的效果。
后端
- Spring boot
- Mybatis
- MySQL
前端
- HTML
- CSS
- JS
- jquery
- ajax
数据交互
前后端通过JSON数据交互。
整体实现思路
- 列表中选择一个自己要聊天的对象
- 使用 ajax 显示消息
- 发送消息 ,使用 ajax 避免刷新,实时显示
- 长轮询,每 2 秒发送一次查询请求并实时显示消息
- 确保每次直达底部的最新消息
实时接收消息
@ResponseBody
@GetMapping("/userCenter/chat/{receiver}")
public String chatWithOthers(@PathVariable("receiver") String receiver,
Model model, HttpSession session) {
//搜索用户的聊天状态
User user = (User) session.getAttribute("loginUser");
String sender = user.getUserName();
List<ChatListDTO> chatLists = chatListService.list(sender);
model.addAttribute("chatLists", chatLists);
if (userMapper.selectUserByName(receiver)==null){
model.addAttribute("noUser","noUser");
return "";
}
//更改消息数的时候,receiver是发送者,而sender是接收者
messageMapper.updateMessageCount(receiver,sender);
//查找属于sender的两人会话
List<MessageDTO> list = messageService.messagesList(sender, receiver, sender);
JSONArray array= JSONArray.parseArray(JSON.toJSONString(list));
return array.toJSONString();
}
html前端
这里是从后端查询了当前用户的聊天列表,并进行遍历显示。
<ul>
<li th:id="${item.chatList.others}" th:each="item : ${chatLists}" class="chat-item">
<!-- th:href="@{'/userCenter/communicate/'+${item.chatList.others}}"-->
<span th:onclick="chat([[${item.chatList.others}]])">
<img th:src="${item.others.imgUrl}" class="chat-img">
<span class="chat-user">
[[${item.chatList.others}]]
</span>
</span>
<span class="chat-time"
th:text="${#dates.format(item.chatList.lastReplyTime,'yyyy-MM-dd HH:mm')}">
</span>
</li>
</ul>
js 和 ajax
name 已经在 onclick 里面传过。
整体思路:往后端发送请求查询,然后往前端返回 JSON ,然后遍历显示。
鉴于会有新信息,要实时接收新信息,就可以使用 setInterval( 一个显示的 function (),2000),这表示两秒查询一次并回显。
function chat(name) {
$("#chat_content").val("");
$("#receiver_name").attr("value", name.toString());
$(".chat-item").removeClass("background");
$("#" + name).addClass("background");
var others = name;
var totol;
var chatArea = $("#chat_area");
var time = 0;
$("#noChat").hide();
$("#chat").css("display", "block");
$.ajax({
type: "get",
url: "/userCenter/chat/" + others,
dataType: "json",
success: function (data) {
chatArea.html("");
data = JSON.stringify(data);
var jsonData = JSON.parse(data);
totol = jsonData.length;
for (var i = 0; i < totol; i++) {
var msg = jsonData[i];
var sender = msg.sender.userName;
var imgUrl = msg.sender.imgUrl;
var content = msg.message.content;
var div_class;
var img_class;
var msg_time = msg.message.gmtCreate;
if (msg_time - time > 60000) {
time = msg_time;
msg_time = new Date(msg_time).toLocaleString().replace(/:\d{1,2}$/, ' ');
} else {
msg_time = "";
}
if (sender != others) {
//如果我是发送者
div_class = "sender";
img_class = "reply-img";
} else {
div_class = "receiver";
img_class = "receiver-img";
}
var message = "<br style=\"line-height: 60px\">" +
"<p style='text-align: center'>"
+ msg_time
+ "</p>" +
"<div class=\"msg-div\">" +
"<div class=" + div_class + ">" +
"<img src= "
+ imgUrl +
" class=" + img_class + ">" +
"<div class=\"content-msg\">"
+ content +
"</div>" +
"</div></div>";
chatArea.append(message);
chatArea.scrollTop(9999);
}
},
error: function () {
alert("错误 ")
}
})
setInterval(function () {
var times = 0;
$.ajax({
type: "get",
url: "/userCenter/chat/" + others,
dataType: "json",
success: function (data) {
data = JSON.stringify(data);
var times = 0;
var jsonData = JSON.parse(data);
var newTotal = jsonData.length;
if (newTotal == totol) {
console.log("无新消息");
} else {
chatArea.html("");
totol = newTotal;
for (var i = 0; i < totol; i++) {
var msg = jsonData[i];
var sender = msg.sender.userName;
var imgUrl = msg.sender.imgUrl;
var content = msg.message.content;
var div_class;
var img_class;
var msg_time = msg.message.gmtCreate;
if (msg_time - times > 60000) {
times = msg_time;
msg_time = new Date(msg_time).toLocaleString().replace(/:\d{1,2}$/, ' ');
} else {
msg_time = "";
}
if (sender != others) {
//如果我是发送者
div_class = "sender";
img_class = "reply-img";
} else {
div_class = "receiver";
img_class = "receiver-img";
}
console.log(img_class);
var message = "<br style=\"line-height: 60px\">" +
"<p style='text-align: center'>"
+ msg_time
+ "</p>" +
"<div class=\"msg-div\">" +
"<div class=" + div_class + ">" +
"<img src= "
+ imgUrl +
" class=" + img_class + ">" +
"<div class=\"content-msg\">"
+ content +
"</div>" +
"</div></div>";
$("#chat_area").append(message);
$("#chat_area").scrollTop(9999);
}
}
},
error: function () {
alert("错误 ")
}
})
}, 2000);
}
实时发送消息
因为显示消息是使用ajax实现的,所以我们就可以通过一个标签来获得聊天对象的名字(id)。在显示聊天信息的时候已经把下面的值改变了。
<span id="receiver_name" value=""></span>
HTML
这里的表单最开始使用的是form,但是因为调用ajax的时候的 success 和 error 都没有执行,就把 form 变成了 div,所有的样式还是和form一样的。
<div id="chat_form" class="padding-top-30px">
<textarea id="chat_content" name="chatContent" class="form-control"
placeholder="给对方打个招呼吧!" cols="20" rows="8"></textarea>
<br>
<button type="submit" onclick="sendMsg()" id="content_submit"
class="btn btn-info btn-primary btn-right">提交
</button>
</div>
js
function sendMsg() {
var chatContent =$("#chat_content");
var receiverName = $("#receiver_name").attr("value");
var content = chatContent.val();
var chatArea = $("#chat_area");
$.ajax({
type: "post",
url: "/chat/" + receiverName,
data:{
"content": content
},
async:false,
dataType: "text",
success:function (data) {
chatContent.val('');
var jsonData = JSON.stringify(data);
var msg1 = jsonData.message;
var msgContent = msg1.content;
var sender1 = jsonData.sender;
var msg_time = jsonData.gmtCreate;
msg_time = new Date(msg_time).toLocaleString().replace(/:\d{1,2}$/, ' ');
var imgUrl = sender1.imgUrl;
var div_class = "sender";
var img_class = "reply-img";
var message = "<br style=\"line-height: 60px\">" +
"<p style='text-align: center'>"
+ msg_time
+ "</p>" +
"<div class=\"msg-div\">"
"<div class=" + div_class + ">" +
"<img src= "
+ imgUrl +
" class=" + img_class + ">" +
"<div class=\"content-msg\">"
+ msgContent +
"</div>" +
"</div></div>";
chatArea.append(message);
chatArea.scrollTop(9999);
},
error:function (data) {
data = JSON.stringify(data);
alert("failed " + data);
}
})
}
ajax对应的方法
@ResponseBody
@PostMapping(value = "/chat/{receiver}")
public String sendMassage(@PathVariable("receiver") String receiver,
@RequestParam String content,
HttpSession session) {
MessageDTO dto = new MessageDTO();
User user = (User) session.getAttribute("loginUser");
String sender = user.getUserName();
Long current;
//创建两个人的消息
for (int i = 1; i <= 2; i++) {
Message msg = new Message();
current = System.currentTimeMillis();
String id = sender + "-" + receiver + "-" + current;
msg.setId(id);
msg.setSender(sender);
msg.setReceiver(receiver);
msg.setHasRead(0);
msg.setContent(content);
msg.setGmtCreate(current);
if (i == 1) {
msg.setForUser(sender);
User senderUser = userMapper.selectUserByName(sender);
dto.setSender(senderUser);
dto.setMessage(msg);
} else {
msg.setForUser(receiver);
User receiveUser = userMapper.selectUserByName(receiver);
dto.setSender(receiveUser);
}
//插入消息
messageMapper.insertMsg(msg);
}
if (chatListMapper.selectChatId(sender, receiver) == null)
//插入到会话列表
{
for (int i = 1; i <=2 ; i++) {
ChatList chatList = new ChatList();
current = System.currentTimeMillis();
chatList.setId(sender + current);
chatList.setLastReplyTime(current);
//循环为两个人的对话赋值
if (i == 1){
chatList.setMe(sender);
chatList.setOthers(receiver);
}
else{
chatList.setMe(receiver);
chatList.setOthers(sender);
}
chatListMapper.insertChatList(chatList);
}
}else {
current = System.currentTimeMillis();
chatListMapper.updateLastReplyTime(sender,receiver,current);
}
String json = JSON.toJSONString(dto);
return json;
}
注意点
在 ajax 中,把DataType设置成“josn”的时候,传往后台的数据 ——“你好”,会自动变成 “content = 你好”, 暂时不清楚是什么原因导致的。在更改了DataType = “text” 后,就可以正常的往后台传送参数了。
效果图
有问题请看这里
有疑问的话可以在评论区留言或者私信作者。
整个系统是一个 医学科普 + 论坛 , 如果需要作为毕业设计的话可以联系作者。