Spring和WebSocket整合详解

Spring和WebSocket整合详解

本篇编写日期较早,代码冗余较多,新博文:Spring和WebSocket整合并建立简单的Web聊天室

官方主页

Spring WebSocket

概述

WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。
这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。

Web浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。由于 WebSockets 连接长期存在,与典型的HTTP连接不同,对服务器有重要的影响。

基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器。

这里,我们基于Spring整合原生的WebSocket,实现简单的IM聊天功能。

Git地址:
Gitee

项目地址:
品茗IT-同步发布

品茗IT 提供在线支持:

一键快速构建Spring项目工具

一键快速构建SpringBoot项目工具

一键快速构建SpringCloud项目工具

一站式Springboot项目生成

如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以加入我们的java学习圈,点击即可加入,共同学习,节约学习时间,减少很多在学习中遇到的难题。

开始搭建

依赖Jar包
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-websocket</artifactId>
	<version>${spring.version}</version>
</dependency>
spring-websocket.xml

该xml文件仅作为配置文件的引入使用。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
                     http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                     http://www.springframework.org/schema/aop
                     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
                     http://www.springframework.org/schema/context
                     http://www.springframework.org/schema/context/spring-context-4.0.xsd ">

	<bean id="annotationPropertyConfigurerWebSocket"
		  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="order" value="1" />
		<property name="ignoreUnresolvablePlaceholders" value="true" />
		<property name="locations">
			<list>
				<value>classpath:websocket.properties</value>
			</list>
		</property>
	</bean>
	
</beans>

websocket.properties保存一些自定义配置,这里没用上,就不贴出来了。

WebSocket配置监听

Spring整合WebSocket需要配置websocket的监听url。

SpringWebSocketConfig:

package com.cff.springwork.websocket.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.cff.springwork.websocket.handler.WebsocketHandler;


@Configuration
@EnableWebMvc
@EnableWebSocket
public class SpringWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(webSocketHandler(),"/websocket").addInterceptors(new WebSocketInterceptor());
        registry.addHandler(webSocketHandler(), "/sockjs").addInterceptors(new WebSocketInterceptor()).withSockJS();
    }
 
    @Bean
    public TextWebSocketHandler webSocketHandler(){
        return new WebsocketHandler();
    }

}
WebSocket的session拦截处理

WebSocketInterceptor对WebSocket的连接进行过滤,可以对连接前和连接后自定义处理。

WebSocketInterceptor:

package com.cff.springwork.websocket.config;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.springframework.http.server.ServerHttpRequest;  
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;  
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;  

@Component
public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor{  
  
    @Override  
    public boolean beforeHandshake(ServerHttpRequest request,  
            ServerHttpResponse response, WebSocketHandler wsHandler,  
            Map<String, Object> attributes) throws Exception {  
        System.out.println("Before Handshake");  
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpSession session = servletRequest.getServletRequest().getSession(false);
            if (session != null) {
                //使用userName区分WebSocketHandler,以便定向发送消息
                String userName = (String) session.getAttribute("userId");
                if (userName==null) {
                    userName="default";
                }
                attributes.put("userId",userName);
            }
        }
        return super.beforeHandshake(request, response, wsHandler, attributes);  
    }  
  
    @Override  
    public void afterHandshake(ServerHttpRequest request,  
            ServerHttpResponse response, WebSocketHandler wsHandler,  
            Exception ex) {  
        System.out.println("After Handshake");  
        super.afterHandshake(request, response, wsHandler, ex);  
    }  
  
}  
消息处理(处理逻辑配合下面的聊天室)

WebsocketHandler负责处理消息发送接收的逻辑:

package com.cff.springwork.websocket.handler;

import java.io.IOException;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;  
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.cff.springwork.websocket.memory.WebSocketUser;
  
public class WebsocketHandler extends TextWebSocketHandler {  
	protected Logger logger = LoggerFactory.getLogger(getClass());
	public static WebSocketUser users = new WebSocketUser();
	public WebsocketHandler(){
		
	}
	
	
    @Override  
    protected void handleTextMessage(WebSocketSession session,  
            TextMessage message) throws Exception {  
    	logger.info("websocketmsg:{}",message.toString()); 
        super.handleTextMessage(session, message);  
        String msg = message.getPayload();
        logger.info("websocketmsg1:{}",msg); 
        if(StringUtils.isNotEmpty(msg)){
        	String param[] = msg.split("\\|");
        	String userName = (String)session.getAttributes().get("userName");
        	sendMessageToUser(param[1], new TextMessage(param[0]+"|"+userName+"|"+param[2]));
        }
//        TextMessage returnMessage = new TextMessage(message.getPayload()+" received at server");  
//        session.sendMessage(returnMessage);  
    }

	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		String userName = (String)session.getAttributes().get("userName");
		String msgType="0000";
		if(userName == null || "".equals(userName)){
			long id = users.getWebsocketId();
			logger.info("random UserId:{}",id); 
			users.putUserNameAndWebSocketSession(id+"", session);
			
			session.sendMessage(new TextMessage(msgType+"|"+ id));
		}else{
			logger.info("UserName:{}",userName); 
			//users.putNickNameAndWebSocketSession(userInfo.getNickName(), session);
			users.putUserNameAndWebSocketSession(userName, session);
			session.sendMessage(new TextMessage(msgType+"|"+userName));
		}
		

		List<TextMessage> message = WebsocketHandler.getOfflineMsg(userName);
	    if( message !=null && message.size()!=0){
	    	for(int i = 0; i < message.size(); i++){
	    		WebsocketHandler.sendMessageToUser(userName, message.get(i));
	    	}
	    }
	}

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
//		
//		super.afterConnectionClosed(session, status);
		users.removeWebSocketSession(session);
		
	}
    
	/**
     * 给某个用户发送消息
     *
     * @param userName
     * @param message
     */
    public static void sendMessageToUser(String userName, TextMessage message) {
    	List<WebSocketSession> webUsers = users.getWebSocketSessionWithUserName(userName);
    	System.out.println(webUsers);
    	System.out.println(userName);
    	if (webUsers == null || webUsers.size() == 0){
    		users.putOfflineMsg(userName, message);
    		return;
    	}
    	for(int i =0; i < webUsers.size(); i++){
	    	WebSocketSession user = webUsers.get(i);
	    	System.out.println(user);
	    	
	        try {
	            if (user.isOpen()) {
	            	user.sendMessage(message);
	            }
	        } catch (IOException e) {
	            e.printStackTrace();
	        } 
    	}
    }
    
    
    /**
     * 给某个用户发送消息
     *
     * @param userName
     * @param message
     */
    public static void sendBinaryMessageToUser(String userName, BinaryMessage message) {
    	List<WebSocketSession> webUsers = users.getWebSocketSessionWithUserName(userName);
    	System.out.println(webUsers);
    	System.out.println(userName);
    	if (webUsers == null || webUsers.size() == 0){
    		//users.putOfflineMsg(userName, message);
    		return;
    	}
    	for(int i =0; i < webUsers.size(); i++){
	    	WebSocketSession user = webUsers.get(i);
	    	System.out.println(user);
	    	
	        try {
	            if (user.isOpen()) {
	            	user.sendMessage(message);
	            }
	        } catch (IOException e) {
	            e.printStackTrace();
	        } 
    	}
    }
    
    public static List<TextMessage> getOfflineMsg(String userName){
    	return users.getOfflineMsgWithUserName(userName);
    }
}  

WebSocketUser是使用内存来存储用户:

package com.cff.springwork.websocket.memory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

public class WebSocketUser {
	private static Map<String,List<WebSocketSession>> userNameWebsession = null;
	private static Map<WebSocketSession,String> webSessionUserName = null;
	private static Map<String,List<TextMessage>> userNameWithOfflineMsg = null;
	private static long websocketId = 100000000L;
	
	public WebSocketUser(){
		userNameWebsession = new ConcurrentHashMap<String,List<WebSocketSession>>();
		webSessionUserName = new ConcurrentHashMap<WebSocketSession,String>();
		userNameWithOfflineMsg = new ConcurrentHashMap<String,List<TextMessage>>();
	}
	
	public Map<String,List<WebSocketSession>> getUserNameWebsession(){
		return userNameWebsession;
	}
	
	public Map<WebSocketSession,String> getWebSessionUserName(){
		return webSessionUserName;
	}
	
	public String getUserNameWithWebSocketSession(WebSocketSession webSocketSession){
		return webSessionUserName.get(webSocketSession); 
	}

	public static Map<String, List<TextMessage>> getUserNameWithOfflineMsg() {
		return userNameWithOfflineMsg;
	}

	public static void setUserNameWithOfflineMsg(Map<String, List<TextMessage>> userNameWithOfflineMsg) {
		WebSocketUser.userNameWithOfflineMsg = userNameWithOfflineMsg;
	}

	public static void setUserNameWebsession(Map<String, List<WebSocketSession>> userNameWebsession) {
		WebSocketUser.userNameWebsession = userNameWebsession;
	}

	public static void setWebSessionUserName(Map<WebSocketSession, String> webSessionUserName) {
		WebSocketUser.webSessionUserName = webSessionUserName;
	}

	/**
	 * 根据昵称拿WebSocketSession
	 * @param nickName
	 * @return
	 */
	public List<WebSocketSession> getWebSocketSessionWithUserName(String userName){
		return userNameWebsession.get(userName);
	}
	
	/**
	 * 移除失效的WebSocketSession
	 * @param webSocketSession
	 */
	public synchronized void removeWebSocketSession(WebSocketSession webSocketSession){
		if (webSocketSession == null) return;
		String nickName = webSessionUserName.get(webSocketSession);
		webSessionUserName.remove(webSocketSession);
		List<WebSocketSession> webSessoin = userNameWebsession.get(nickName);
		if (webSessoin == null) return;
		webSessoin.remove(webSocketSession);
	}
	
	/**
	 * 存放离线消息
	 * @param nickName
	 * @param msg
	 */
	public synchronized void putOfflineMsg(String userName, TextMessage msg){
		if (userNameWithOfflineMsg.get(userName) == null){
			List<TextMessage> msgList = new ArrayList<TextMessage>();
			msgList.add(msg);
			userNameWithOfflineMsg.put(userName, msgList);
		}else{
			List<TextMessage> msgList = userNameWithOfflineMsg.get(userName);
			msgList.add(msg);
		}
	}
	
	/**
	 * 根据昵称拿离线消息
	 * @param nickName
	 * @return
	 */
	public List<TextMessage> getOfflineMsgWithUserName(String userName){
		return userNameWithOfflineMsg.get(userName);
	}
	
	/**
	 * 存放昵称和WebSocketSession
	 * @param nickName
	 * @param webSocketSession
	 */
	public synchronized void putUserNameAndWebSocketSession(String userName, WebSocketSession webSocketSession){
		webSessionUserName.put(webSocketSession, userName);
		if (userNameWebsession.get(userName) == null){
			List<WebSocketSession> webSessoin = new ArrayList<WebSocketSession>();
			webSessoin.add(webSocketSession);
			userNameWebsession.put(userName, webSessoin);
		}else{
			List<WebSocketSession> webSessoin = userNameWebsession.get(userName);
			webSessoin.add(webSocketSession);
		}
	}
	
	/**
	 * 移除失效的WebSocketSession
	 * @param webSocketSession
	 */
	public synchronized long getWebsocketId(){
		websocketId += 1;
		return websocketId;
	}
	
	public static void main(String srga[]){
		String test = "123456";
		
		Boolean flag = true;
		if(test == "012345678".substring(1, 7)){
			flag = false;
		}
		System.out.println(flag);
	}
}
聊天室

为了实现我们的简单聊天功能,我们需要前端进行配合。

websocketchat.jsp实现了简单的聊天室,支持文字、表情、文件等:

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<%@ include file="/common/head.jsp"%>
<!-- <script src="http://www.jq22.com/js/jq.js"></script> -->
<script type="text/javascript" src="${ctx}/js/jquery/jquery-3.2.1.min.js" ></script>
<script src="${ctx}/js/websocket/websocket.js"></script>
<script src="${ctx}/js/jquery/ajaxfileupload.js"></script>
<script src="${ctx}/js/jquery/jquery-browser.js"></script>
<script src="${ctx}/js/jquery/jquery.qqFace.js"></script>
<head>
<title>聊天室</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="content-script-type" content="text/javascript">
<meta http-equiv="content-style-type" content="text/css">
<style rel="stylesheet" type="text/css" media="all" />
body { text-align:left; margin:0; font:normal 12px Verdana, Arial;
background:#FFEEFF } form { margin:0; font:normal 12px Verdana,
Arial; } table,input { font:normal 12px Verdana, Arial; }
a:link,a:visited{ text-decoration:none; color:#333333; } a:hover{
text-decoration:none; color:#FF6600 } #main { width:400px;
position:absolute; left:600px; top:100px; background:#EFEFFF;
text-align:left; filter:Alpha(opacity=90) } #ChatHead {
text-align:right; padding:3px; border:1px solid #003399;
background:#DCDCFF; font-size:20px; color:#3366FF; cursor:move; }
#ChatHead a:link,#ChatHead a:visited, { font-size:14px;
font-weight:bold; padding:0 3px } #ChatBody { border:1px solid
#003399; border-top:none; padding:2px; } #ChatContent {
height:200px; padding:6px; overflow-y:scroll; word-break: break-all
}#ChatBtn { border-top:1px solid #003399; padding:2px }
</style>
<script language="javascript" type="text/javascript">
var ws = null;
var msgUser=null;
var muserId = null;
var nickName ="";
var contextPath = '${ctx}';
var imgName = null;
var fileImgSize = 0;
window.onbeforeunload = function()
{
	disconnect(ws);
}
function ChatSetUser(user,userId) {
	if(muserId != null && muserId != userId){
		$("#ChatContent").html("");
	}
	msgUser = user;
	muserId = userId;
	imgName = msgUser;
	//alert("owerId:" + nickName + "-----msgUser:" + msgUser);
	//alert("userId:" + nickName + "-----userId:" + muserId);
	if (msgUser == nickName) {
		return;
	}
	if (msgUser == "") {
		return;
	}
	ChatNew();
}
function gs(d) {
	var t = document.getElementById(d);
	if (t) {
		return t.style;
	} else {
		return null;
	}
}
function gs2(d, a) {
	if (d.currentStyle) {
		var curVal = d.currentStyle[a]
	} else {
		var curVal = document.defaultView
				.getComputedStyle(d, null)[a]
	}
	return curVal;
}
function ChatHidden() {
	gs("ChatBody").display = "none";
}
function ChatShow() {
	gs("ChatBody").display = "";
}
function ChatClose() {
	gs("main").display = "none";
	//disconnect(ws);
}
function ChatNew() {
	gs("main").display = "";
	$("#ChatUsers").html(msgUser);
	$('.emotion').qqFace({

		id : 'facebox', 

		assign:'saytext', 

		path: contextPath+'/img/arclist/'	//表情存放的路径

	});
}
function ChatClear(obj) {
	$("#ChatContent").html("");
}

function ChatRead() {
	document.getElementById(msgUser).setAttribute('src', contextPath+'/img/users.png');
}

function ChatSend(obj) {
	var o = obj.ChatValue;
	var msg = replace_em(o.value);
	if (o.value.length > 0) {
		$("#ChatContent").append(
				"<p align=\"right\"><strong>" + nickName + "(我) :</strong>" + msg
						+ "</p>");
		var number = $("#ChatContent").scrollTop();
    	number += 16;
    	$("#ChatContent").scrollTop(number);
    	if(ws!=null){
    		ws.send("0001" + "|" + muserId + "|" + encodeURI(o.value));
    	}
		o.value = '';
	}
	
	var img = obj.ChatFile;
	if (img.value.length > 0){
		$("#ChatContent").append(
				"<p align=\"right\"><strong>" + nickName + "(我) :</strong>" + img.value
						+ "</p><br/>");

		imgName = nickName+'(我)';
		fileImgSize = img.files.length;
		//alert(fileImgSize);
		$.ajaxFileUpload({
			//处理文件上传操作的服务器端地址(可以传参数,已亲测可用)
			url:'${ctx}/webSocket/fileUpload?userId='+muserId,
			secureuri:true,                       //是否启用安全提交,默认为false 
			fileElementId:'ChatFile',           //文件选择框的id属性
			dataType:'text',                       //服务器返回的格式,可以是json或xml等
			success:function(data, status){        //服务器响应成功时的处理函数
				//$("#ChatContent").append("<p align=\"right\">" + data + "</p><br/>");
			},
			error:function(data, status, e){ //服务器响应失败时的处理函数
				$("#ChatContent").append('<p align=\"right\">图片上传失败,请重试!!</p><br/>');
				imgName = msgUser;
			}
		});
	}
}
	if (document.getElementById) {
		(function() {
			if (window.opera) {
				document.write("<input type='hidden' id='Q' value=' '>");
			}

			var n = 500;
			var dragok = false;
			var y, x, d, dy, dx;

			function move(e) {
				if (!e)
					e = window.event;
				if (dragok) {
					d.style.left = dx + e.clientX - x + "px";
					d.style.top = dy + e.clientY - y + "px";
					return false;
				}
			}

			function down(e) {
				if (!e)
					e = window.event;
				var temp = (typeof e.target != "undefined") ? e.target
						: e.srcElement;
				if (temp.tagName != "HTML" | "BODY"
						&& temp.className != "dragclass") {
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
				}
				if ('TR' == temp.tagName) {
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
					temp = (typeof temp.parentNode != "undefined") ? temp.parentNode
							: temp.parentElement;
				}

				if (temp.className == "dragclass") {
					if (window.opera) {
						document.getElementById("Q").focus();
					}
					dragok = true;
					temp.style.zIndex = n++;
					d = temp;
					dx = parseInt(gs2(temp, "left")) | 0;
					dy = parseInt(gs2(temp, "top")) | 0;
					x = e.clientX;
					y = e.clientY;
					document.onmousemove = move;
					return false;
				}
			}

			function up() {
				dragok = false;
				document.onmousemove = null;
			}

			document.onmousedown = down;
			document.onmouseup = up;

		})();
	}
	
	function toIndex(){
		window.location.href= contextPath + "/index.jsp";
	}

	$(function() {
		if (ws == null) {
			var url = getUrl();
		    //alert("url:"+url);  
		    if (!url) {  
		        return;  
		    }  

		    ws = new WebSocket(url);  
			connect(ws);
		}
		ChatClose();
// 		ChatSetUser("human","16888888888");
	})
</script>
</head>

<body>
	<div>
		<div style="display: inline;">
			<img style="width: 5%;margin-left: 100px;" alt="" src="${ctx}/img/logo.png" onclick="toIndex()"/>
		</div>
		<div style="display: inline; float: right">
			<img style="width: 220px;display:block;margin-top: 15px;margin-right: 200px;"  alt="" src="${ctx}/img/logoright.png"/>
		</div>
	</div>
	<div id="main" class="dragclass" onclick="ChatRead()" style="left: 400px; top: 200px;">
		<div id="ChatUsers" style="width:100px; padding:3px; font-size:15px;float:left; display:inline"></div>
		<div id="ChatHead">
			<a href="#" onclick="ChatHidden();">-</a> <a href="#"
				onclick="ChatShow();">+</a> <a href="#" onclick="ChatClose();">x</a>
		</div>
		<div id="ChatBody">
			<div id="ChatContent"></div>
			<div id="ChatBtn">
				<form action="" name="chat" method="post">
					<textarea name="ChatValue" id="saytext" rows="3" style="width: 350px"></textarea>
					<input name="Submit" type="button" value="发送"
						onclick="ChatSend(this.form);" />
					<input name="ClearMsg" type="button" value="清空记录"
						onclick="ChatClear(this.form);" />
					<input type="button" class="emotion" value="表情">
					<input id="ChatFile" type="file" name="myfiles"  multiple>   
				</form>
			</div>
		</div>
	</div>
	<div align="left" style="margin-top: 50px;margin-left: 20px;">
		<p id=RandomContent>欢迎您,15607110725</p>
		<p id=content></p>
	</div>
	
</body>
</html>

这个jsp需要websocket.js配合:

function getUrl() {     
	var index = contextPath.lastIndexOf("/");
	var urlPath = contextPath.substring(index, contextPath.length) + "/websocket";
	if (window.location.protocol == 'http:') {  
		url = 'ws://' + window.location.host + urlPath;  
	} else {  
		url = 'wss://' + window.location.host + urlPath;  
	}   
	return url;
} 

function disconnect(ws) {  
    if (ws != null) {  
        ws.close();  
        ws = null;  
    }  
}  

function replace_em(str){

//	str = str.replace(/\</g,'&lt;');
//
//	str = str.replace(/\>/g,'&gt;');
//
//	str = str.replace(/\n/g,'<br/>');

	str = str.replace(/\[em_([0-9]*)\]/g,'<img src="../../img/arclist/$1.gif" border="0" />');

	return str;

}

function connect(ws) { 

    ws.onopen = function () {  
    	$("#ChatContent").append("<small>连接成功。。。</small><br>");
    };  
    ws.onmessage = function (event) {
    	if(typeof(event.data)=="string"){  
	    	var dataAll = event.data;
	    	var indexMsgType = dataAll.indexOf("|");
	    	var msgType = dataAll.substring(0,indexMsgType);
	    	console.log('dataAll:'+dataAll);
	    	if(msgType == "0000"){
	    		webid = dataAll.substring(indexMsgType+1,dataAll.length);
	    		$("#RandomContent").html("欢迎您,"+webid);
	    		nickName = webid;
	    	}else{
	    		var data = dataAll.substring(indexMsgType+1,dataAll.length);
		    	var index = data.indexOf("|");
		    	var userId = data.substring(0,index);
		    	var msg = decodeURI(data.substring(index+1,data.length));
		    	var result = replace_em(msg);
		    	if(document.getElementById(userId)){
		    		document.getElementById(userId).setAttribute('src', contextPath+'/img/msgget.gif');
			    	var number = $("#ChatContent").scrollTop();
		    		//var number = $("#ChatContent").height();
			    	number += 15;
			    	$("#ChatContent").scrollTop(number);
			    	$("#ChatContent").append("<strong>"+userId+" :</strong>" + result + "<br>");
		    	}else{
			    	//var content = $("#content").html();
			    	content = "<img src=\""+contextPath + "/img/msgget.gif\" id=\""
						+ userId
						+ "\" alt=\"\" style=\"cursor: pointer\" width='40px' "
						+ "οnclick=\"ChatSetUser('"
						+ userId
						+ "','"
						+ userId
						+ "')\" />"
						+ userId
						+ "<br><br>";
			    	$("#content").append(content);
			    	$("#ChatContent").append("<strong>"+userId+" :</strong>" + result + "<br>");
		    	}
		    	
	    	}
	    }else{  
    	  var reader = new FileReader();  
    	  reader.onload = function(event){  
    	       if(event.target.readyState == FileReader.DONE){  
    	            var url = event.target.result;  
    	            if (imgName != msgUser){
    	            	$("#ChatContent").append("<p align=\"right\"><strong>"+imgName+" :</strong>"+"<img src = "+url+" width='100px'/></p><br>");  
    	       		}else{
    	            	$("#ChatContent").append("<strong>"+imgName+" :</strong>"+"<img src = "+url+" width='100px'/><br>");  
    	       		}
    	            if (fileImgSize != 0){
    	            	fileImgSize = fileImgSize - 1;
    	            }else{
    	            	imgName = msgUser;
    	            }
    	       }
    	   }  
	      reader.readAsDataURL(event.data);  
	    }  
    };  
    ws.onclose = function (event) {  
        //alert('网络连接失败!');  
    };  
} 

以上已经支持表情和文字发送,要支持图片,需要我们自定义发送文件功能WebSocketController:

package com.cff.springwork.websocket.endpoint;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.socket.BinaryMessage;

import com.cff.springwork.websocket.handler.WebsocketHandler;

@Controller("webSocketController")
@RequestMapping("/webSocket")
public class WebSocketController {
	protected Logger logger = LoggerFactory.getLogger(getClass());
	
	@RequestMapping(value="/fileUpload")
	public void fileUpload(@RequestParam("userId") String userId, @RequestParam MultipartFile[] myfiles, HttpServletRequest request, HttpServletResponse response) throws IOException{
		//可以在上传文件的同时接收其它参数
		System.out.println("收到发往用户[" + userId + "]的文件上传请求");
		
		//如果用的是Tomcat服务器,则文件会上传到\\%TOMCAT_HOME%\\webapps\\YourWebProject\\upload\\文件夹中
		//这里实现文件上传操作用的是commons.io.FileUtils类,它会自动判断/upload是否存在,不存在会自动创建
		String realPath = request.getSession().getServletContext().getRealPath("/upload");
		System.out.println("路径[" + realPath + "]的文件上传请求");
		System.out.println("文件数量:"+myfiles.length);
		realPath = "D:/doc/";
		//设置响应给前台内容的数据格式
		response.setContentType("text/plain; charset=UTF-8");
		//上传文件的原名(即上传前的文件名字)
		String originalFilename = null;
		//如果只是上传一个文件,则只需要MultipartFile类型接收文件即可,而且无需显式指定@RequestParam注解
		//如果想上传多个文件,那么这里就要用MultipartFile[]类型来接收文件,并且要指定@RequestParam注解
		//上传多个文件时,前台表单中的所有<input type="file"/>的name都应该是myfiles,否则参数里的myfiles无法获取到所有上传的文件
		for(MultipartFile myfile : myfiles){
			if(myfile.isEmpty()){
				response.setCharacterEncoding("UTF-8");
				response.getWriter().write("102");
				return;
			}else{
				originalFilename = myfile.getOriginalFilename();
				System.out.println("文件原名: " + originalFilename);
				System.out.println("文件名称: " + myfile.getName());
				System.out.println("文件长度: " + myfile.getSize());
				System.out.println("文件类型: " + myfile.getContentType());
				System.out.println("========================================");
				try {
					//这里不必处理IO流关闭的问题,因为FileUtils.copyInputStreamToFile()方法内部会自动把用到的IO流关掉
					//此处也可以使用Spring提供的MultipartFile.transferTo(File dest)方法实现文件的上传
					//FileUtils.copyInputStreamToFile(myfile.getInputStream(), new File(realPath, originalFilename));
					
					ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); 
					byte[] buff = new byte[100]; //buff用于存放循环读取的临时数据 
					int rc = 0; 
					InputStream is = myfile.getInputStream();
					while ((rc = is.read(buff, 0, 100)) > 0) { 
					swapStream.write(buff, 0, rc); 
					} 
					byte[] in_b = swapStream.toByteArray(); //in_b为转换之后的结果
					System.out.println("正在发送文件: ");

					WebsocketHandler.sendBinaryMessageToUser(userId,new BinaryMessage(in_b));
				} catch (IOException e) {
					System.out.println("文件[" + originalFilename + "]上传失败,堆栈轨迹如下");
					e.printStackTrace();
					response.setCharacterEncoding("UTF-8");
					response.getWriter().write("101");
					return ;
				}
			}
		}
		response.setCharacterEncoding("UTF-8");
		response.getWriter().write("200");
		return;
	}
	
}

详细完整代码,可以在Spring组件化构建中选择查看,并下载。

快速构建项目

Spring组件化构建

喜欢这篇文章么,喜欢就加入我们一起讨论Spring技术吧!
品茗IT交流群

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值