功能列表:
一、Web端的IM應用
二、支持上線、下線、實時在線提醒
三、單聊、羣聊的創建
四、普通文字、表情、圖片的傳輸(子定義富文本)
五、單人的頂級提醒,多對話的窗口的提醒
六、調用圖靈機器人的自動回覆演示
核心技術列表
一、websocket、sockjs、stomp
二、前端展現涉及的jquery、vue、elementUI、jquerybase64js
三、後端springboot、jsoup、spring-security、spring-websocket
成果展現:
技術實現說明:
Websocket部分
web端的IM應用,要想實現兩個客戶端的通訊,必然要經過服務器進行信息的轉發。例如A要和B通訊,則應該是A先把信息發送給IM應用服務器,服務器根據A信息中攜帶的接收者將它再轉發給B,一樣B到A也是這種模式。
而要實現web端的實時通信,websocket也是其中最好的方式,其餘的協議如長輪詢、短輪詢、iframe數據、htmlfile等。
在實際開發中,咱們一般使用的是一些別人寫好的實時通信的庫,好比socket.io、sockjs(咱們本次使用了他,相似jquery,對其餘即時通信技術作了封裝),他們的原理就是將上面(還有一些其餘的如基於Flash的push)的一些技術進行了在客戶端和服務端的封裝,而後給開發者一個統一調用的接口。這個接口在支持websocket的環境下使用websocket,在不支持它的時候啓用上面所講的一些hack技術。
WebSocket是HTML5的一種新通訊協議(ws協議),是一個消息架構,不強制使用任何特定的消息協議,它依賴於應用層解釋消息的含義;與處在應用層的HTTP不一樣,WebSocket處在TCP上很是薄的一層,會將字節流轉換爲文本/二進制消息,所以,對於實際應用來講,WebSocket的通訊形式層級太低,所以,能夠在 WebSocket 之上使用 STOMP協議,來爲瀏覽器 和 server間的 通訊增長適當的消息語義。
STOMP(Simple Text-Orientated Messaging Protocol) 面向消息的簡單文本協議。 同 HTTP 在 TCP 套接字上添加請求-響應模型層同樣,STOMP 在 WebSocket 之上提供了一個基於幀的線路格式層,用來定義消息語義;css
STOMP 源碼http://cdn.bootcss.com/stomp.js/2.3.3/stomp.js,有興趣的能夠看一下能大體瞭解其原理和用法。
本例程序核心代碼:html
<!--TO 建立socket鏈接 並訂閱相關頻道--> var socket = new SockJS('/im-websocket'); stompClient = Stomp.over(socket); //設置stomp 控制檯日誌爲不輸出 stompClient.debug=null; stompClient.connect({}, function (frame) { // 至關於鏈接 ws://localhost:8080/gs-guide-websocket/041/hk5tax0r/websocket hk5tax0r就是sessionid console.log("正在鏈接",socket._transport.url); //訂閱通用私聊頻道 羣組也經過這裏實現 stompClient.subscribe('/user/topic/private', function (greeting) { } ); //訂閱用戶上線下線的公共頻道 stompClient.subscribe('/topic/userlist', function (greeting) { }); },function errorCallBack (error) { // 鏈接失敗時(服務器響應 ERROR 幀)的回調方法 });
數據發送以下:
//第一個參數對應controller的 @MessageMapping註解 /app爲後臺定義的通用前綴
//第三個參數爲內容字符串
stompClient.send("/app/private", {}, JSON.stringify(message));//發送服務器前端
對應服務端部分vue
#WebSocketConfig @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { public List<User> onlineUser=new ArrayList<User>(); @Autowired private SimpMessagingTemplate template; @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setUserDestinationPrefix("/user"); config.setApplicationDestinationPrefixes("/app"); config.setCacheLimit(1048576);//大小 1M } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //註冊的websocket接入點,前端連接的就是它 registry.addEndpoint("/im-websocket").withSockJS(); } @Override public void configureWebSocketTransport(final WebSocketTransportRegistration registration) { //設置 文件緩衝 大小 1M //如不設置文件稍微大一點就報錯了 registration.setMessageSizeLimit(1048576); registration.setSendBufferSizeLimit(1048576); registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() { @Override public WebSocketHandler decorate(final WebSocketHandler handler) { return new WebSocketHandlerDecorator(handler) { @Override public void afterConnectionEstablished(final WebSocketSession session) throws Exception { ****** } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { ***** } }; } }); super.configureWebSocketTransport(registration); } } #contoller @MessageMapping("/private") public void privatechat(ImMessage message) throws Exception { ***** template.convertAndSendToUser(message.getReceiver(),"/topic/private",message); ////發送其訂閱的頻道 ///瀏覽器客戶端,訂閱了’/user/topic/private這條路徑, }
其中@MessageMapping("bar") //@MessageMapping接收客戶端消息
另外@SendTo("/topic/brocast") //@SendTo廣播消息出去
@SendToUser("/topic/greetings")//發送對應人。
這兩個註解能夠用template.convertAndSendTo、template.convertAndSendToUser在代碼實現。html5
Spring-security部分
作了簡單的登陸驗證,登陸成功即在系統存有sessionid。結合上面的訂閱,實際連接的後臺地址會附加上sessionid,也就是httprequest中的登陸受權信息即javax.security.Principal會被綁定到websocket的session中。便可以實現與指定登陸人的雙向連接。java
頁面展現部分
頁面展現核心應用vue,vue很差實現的地方使用的是jquery。樣式採用的elmentUI。
在開發這個im應用測試案例的時候。實現了一些前端效果。核心有用的列到下面,各位也許在後面的學習中可以用到。jquery
1.vue兼容性的問題
由於原本不是webpack開發模式,屬於直接引入js的普通HTML開發,如須要解決vue兼容性問題,能夠引入
https://cdn.bootcss.com/babel... 以解決。webpack
二、vue用法(v-model、@click、v-html、v-forv-if,v-bind)的應用,指令、過濾器、全局方法、watch的使用。其中指令用來實現div的默認焦點。全局方法用來代替過濾器,實現實時的消息內容base64解碼。
三、利用vue核心數據的雙向綁定,不刷新顯示更新。但數據設計爲多層的json數組數據,當底層數據變化,vue不能自動檢測到變化。須要進行手動檢測。
代碼-this.$forceUpdate();web
四、關於圖片消息的用法
用圖片上傳按鈕上,存在透明的fileinput文件,每次上傳完,經過onchange方法,先檢測文件大小、類型後,經過fileReader預覽。這其中涉及富文本框焦點的問題、和base64轉碼的問題。另外當上傳失敗經過.val('')清空file,這樣才能從新選擇文件上傳併成功觸發onchange事件(值得了解,試驗了半天才行)spring
var filec=$("#file"+index); if (filec&&filec.length>0) { var fileList = filec[0].files; if(!/image\/\w+/.test(fileList[0].type)) //判斷獲取的是否爲圖片文件 { this.$message.error('請確保文件爲圖像文件'); //清空input 能夠再次上傳並觸發onchange filec.val('') return false; } if(fileList[0].size>1048576){ this.$message.error('請確保圖片不大於1M'); //清空input 能夠再次上傳並觸發onchange filec.val('') return false; } fileReader.onload = function (e) { // 獲取文件的base64編碼 var base64 = e.target.result var image = new Image(); image.src = base64; image.onload = function() { //文件像素過大,調整爲稍小的 var newW="";var newH=""; if(this.width>this.height&&this.width>200){ newW=200; newH=200/this.width*this.height; } if(this.width<=this.height&&this.height>200){ newH=200; newW=200/this.height*this.width; } var h = '<img src=' + base64 + ' width='+newW+' height='+newH+'>'; _insertimg(h, index)//插入到富文本對應的位置 }; } fileReader.readAsDataURL(fileList[0]);
五、關於富文本在指定焦點位置插入數據的問題,後續能夠考慮baidueditor等成熟產品。
當前富文本主要利用了html5的屬性contenteditable解決的
具體能夠查看_insertimg方法
六、在實現上述富文本的時候,相似插入表情、選擇圖片的時候,只要點擊屏幕,則當前頁面焦點即轉移,影響實際插入的位置。因此須要設置這些按鈕點擊的時候屏蔽默認效果。
一個是按鈕的@click.prvent。另外能夠經過下面的方法解決
document.addEventListener("mousedown", function(e){ if(e.target.id=="emoijT"){ e.preventDefault() } }, false);
七、由於當前發送的消息是帶html標籤的富文本信息,爲避免傳輸的問題,將內容進行base64轉碼,消息被接收後再轉碼回來。
var stompClient = null;
//防止亂碼
$.base64.utf8encode = true;
$.base64.btoa(thisMessage);//使用插件base64編碼
//解碼 $.base64.atob(c, true);
八、當前案例不只實現了多對話窗口,隱藏的對話提醒。也實現了當前人的瀏覽器標題提醒。
var pageMessage = { time: 0, title: document.title, timer: null, // 顯示新消息提示 show: function () { var title = pageMessage.title.replace("【 】", "").replace("【新消息】", ""); // 定時器,設置消息切換頻率閃爍效果就此產生 pageMessage.timer = setTimeout(function () { pageMessage.time++; pageMessage.show(); if (pageMessage.time % 2 == 0) { document.title = "【新消息】" + title } else { document.title = "【 】" + title } ; }, 600); return [pageMessage.timer, pageMessage.title]; }, // 取消新消息提示 v clear: function () { clearTimeout(pageMessage.timer); document.title = pageMessage.title; } };
九、關於機器人自動對話,目前使用jsoup調用的遠程接口,由其返回答案。雖然是免費接口,可是一天不能調用屢次。
String url = "http://www.tuling123.com/openapi/api"; //請填寫本身的key String userid="454995"; String post = "{\"key\": \"646d321c227045a69253fd07d8703840\",\"info\": \""+message.getContent()+"\",\"userid\":\""+userid+"\"}"; String body = Jsoup.connect(url).method(Connection.Method.POST) .requestBody(post) .header("Content-Type", "application/json; charset=utf-8") .ignoreContentType(true).execute().body();