Wechat mini program chat room + websocket + file upload (send pictures)

Recently, buddies are writing WeChat applets. One of the needs is to build a chat room, which can chat with multiple people, private chat, and send pictures. However, because I haven't had any relevant knowledge in this area, I slowly looked at it and did it. It was really difficult in the early stage, and the road was not easy to go.
Let's take a look at the effect first.
Insert picture description here
Insert picture description here
Insert picture description here
1. The websocket package is built.
I built it with Springboot + gradle . This is the gradle configuration.

plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'io.spring.dependency-management' version '1.0.7.RELEASE'
id 'java'
}

group = 'org.ddd'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'

repositories {
    //mavenCentral()
    // �޸IJֿ��ַΪ���ڲֿ�
    maven {
        url 'http://maven.aliyun.com/nexus/content/groups/public/'
    }
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web' // spring boot ����
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.0' // mybatis ����
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-aop', version: '2.1.8.RELEASE'
    compile("org.springframework.boot:spring-boot-starter-websocket")
    runtimeOnly 'mysql:mysql-connector-java' // mysql ����
    testImplementation 'org.springframework.boot:spring-boot-starter-test' // spring boot ����
    compile "org.springframework.boot:spring-boot-starter-thymeleaf"
    compile group: 'org.apache.directory.studio', name: 'org.apache.commons.lang', version: '2.6'
    // https://mvnrepository.com/artifact/com.alibaba/fastjson
    compile group: 'com.alibaba', name: 'fastjson', version: '1.2.58'
}

Then give the websocket code of java first.

	package DPI.service;

	import java.io.IOException;
	import java.util.concurrent.CopyOnWriteArraySet;
	
	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.springframework.stereotype.Component;
	
	//@ServerEndpoint("/websocket/{user}")
	@ServerEndpoint(value = "/websocket/{user}/{otherUser}")
	@Component
	public class MyWebSocketServer {
		//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
		private static int onlineCount = 0;
		//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
		private static CopyOnWriteArraySet<MyWebSocketServer> webSocketSet = new CopyOnWriteArraySet<MyWebSocketServer>();
	
		//与某个客户端的连接会话,需要通过它来给客户端发送数据
		private Session session;
		private String user;
	
		/**
		 * 连接建立成功调用的方法
		 */
		@OnOpen
		public void onOpen(@PathParam(value="user") String param, Session session) {
			System.out.println("连接成功");
			this.session = session;
			this.user = param;
			webSocketSet.add(this); // 加入set中
			
			addOnlineCount(); // 在线数加1
	
			try {
				sendMessage("连接成功");
			} catch (IOException e) {
	
			}
			System.out.println("有新连接加入! 当前在线人数" + onlineCount);
		}
	
		/**
		 * 连接关闭调用的方法
		 */
		@OnClose
		public void onClose() {
			webSocketSet.remove(this); // 从set中删除
			subOnlineCount(); // 在线数减1
			System.out.println("连接关闭");
			System.out.println("有连接关闭! 当前在线人数" + onlineCount);
		}
	
		/**
		 * 收到客户端消息后调用的方法
		 *
		 * @param message 客户端发送过来的消息
		 */
		@OnMessage
		public void onMessage(String message, Session session, @PathParam("user") String user, @PathParam("otherUser") String otherUser) {
			System.out.println("来自" + user + "消息:" + message);
	//		try {
	//			session.getBasicRemote().sendText(message);
	//		} catch (IOException e) {
	//			// TODO Auto-generated catch block
	//			e.printStackTrace();
	//		}
			pushMessage(user, message, otherUser);
		}
		
		/**
	         * 消息推送
	     *
	     * @param message
	     * @param otherUser发送对象    otherUser为空则推送全部人员
	     */
		private void pushMessage(String user, String message, String otherUser) {
			// TODO Auto-generated method stub
			if (otherUser == null || "".equals(otherUser) || otherUser.equals("全部")) {
				//群发消息
				for (MyWebSocketServer item : webSocketSet) {
					try {
						item.sendMessage(user + ":" + message);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
	        } else {
	        	for (MyWebSocketServer item : webSocketSet) {
	        		if (otherUser.equals(item.user)) {
	        			try {
							item.sendMessage(message);
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
	        		}
	        	}
	        }
		}
	
		/**
		 * 
		 * @param session
		 * @param error
		 */
		@OnError
		public void onError(Session session, Throwable error) {
	
			error.printStackTrace();
		}
		
		public void sendMessage(String message) throws IOException {
			this.session.getBasicRemote().sendText(message);
		}
	
		/**
		 * 群发自定义消息
		 */
		public static void sendInfo(String message) throws IOException {
	
			for (MyWebSocketServer item : webSocketSet) {
				try {
					item.sendMessage(message);
				} catch (IOException e) {
					continue;
				}
			}
		}
	
		public static synchronized int getOnlineCount() {
			return onlineCount;
		}
	
		public static synchronized void addOnlineCount() {
			MyWebSocketServer.onlineCount++;
		}
	
		public static synchronized void subOnlineCount() {
			MyWebSocketServer.onlineCount--;
		}
	}

WeChat applet

js:

const app = getApp();
var inputVal = '';
var msgList = [];
var windowWidth = wx.getSystemInfoSync().windowWidth;
var windowHeight = wx.getSystemInfoSync().windowHeight;
var keyHeight = 0;
var socketOpen = false;
var frameBuffer_Data, session, SocketTask;
var url = 'ws://localhost:8000/websocket/';
var upload_url ='http://localhost:8000/file/upload'
/**
 * 初始化数据
 */
function initData(that) {
  inputVal = '';

  msgList = [
    {
      speaker: 'others',
      contentType: 'text',
      content: '你好'
    },
    {
      speaker: 'our',
      contentType: 'text',
      content: '你好'
    },
    {
      speaker: 'others',
      contentType: 'text',
      content: '你有什么问题吗?'
    }
  ]
  that.setData({
    msgList,
    inputVal
  })
}

Page({

  /**
   * 页面的初始数据
   */
  data: {
    scrollHeight: '100%',
    inputBottom: 0,
    otherName:"钟南山",
    inputVal: '',
    imgUrl: ''
  },

  changeOtherName:function(){
    wx.setNavigationBarTitle({
      title:this.data.otherName
    })
  },
  getUserInput: function(e) {
    this.data.inputVal = e.detail.value
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function(options) {
    initData(this);
    this.setData({
      cusHeadIcon: app.globalData.userInfo.avatarUrl,
    });
    this.changeOtherName();
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function() {
    if (!socketOpen) {
      this.webSocket()
    }
  },
  onReady: function () {
    var that = this;
    SocketTask.onOpen(res => {
      socketOpen = true;
      console.log('监听 WebSocket 连接打开事件。', res)
    })
    SocketTask.onClose(onClose => {
      console.log('监听 WebSocket 连接关闭事件。', onClose)
      socketOpen = false;
      this.webSocket()
    })
    SocketTask.onError(onError => {
      console.log('监听 WebSocket 错误。错误信息', onError)
      socketOpen = false
    })
    SocketTask.onMessage(onMessage => {
      console.log(onMessage);
      msgList.push({
        speaker: 'others',
        contentType: 'text',
        content: onMessage.data
      })
      console.log('监听WebSocket接受到服务器的消息事件。服务器返回的消息', onMessage.data)
    })
  },
  webSocket: function () {
    // 创建Socket
    SocketTask = wx.connectSocket({
      url: url + "落花人独立" + '/ccc',
      data: 'data',
      header: {
        'content-type': 'application/json'
      },
      method: 'post',
      success: function (res) {
        socketOpen = true;
        console.log('WebSocket连接创建', res)
      },
      fail: function (err) {
        wx.showToast({
          title: '网络异常!',
        })
        console.log(err)
      },
    })
  },
  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function() {

  },

  /**
   * 获取聚焦
   */
  focus: function(e) {
    keyHeight = e.detail.height;
    this.setData({
      scrollHeight: (windowHeight - keyHeight) + 'px'
    });
    this.setData({
      toView: 'msg-' + (msgList.length - 1),
      inputBottom: keyHeight + 'px'
    })
    //计算msg高度
    // calScrollHeight(this, keyHeight);

  },

  //失去聚焦(软键盘消失)
  blur: function(e) {
    this.setData({
      scrollHeight: '100%',
      inputBottom: 0
    })
    this.setData({
      toView: 'msg-' + (msgList.length - 1)
    })

  },
  submitTo: function () {
    if (socketOpen) {
      console.log('test');
      // 如果打开了socket就发送数据给服务器
      sendSocketMessage(this.data.inputVal)
    }
    msgList.push({
      speaker: 'our',
      contentType: 'text',
      content: this.data.inputVal
    })
    inputVal = '';
    this.setData({
      msgList,
      inputVal
    });
  },
  upImg: function() {
    var that = this;
    wx.chooseImage({
      count: 1,
      sizeType: ['original', 'compressed'],
      sourceType: ['album', 'camera'],
      success: (res) => {
        console.log(res);
        
        that.data.imgUrl = res.tempFilePaths[0]
        if (socketOpen) {
          console.log('test');
          // 如果打开了socket就发送数据给服务器
          sendSocketMessage(this.data.imgUrl)
        }
        console.log('uploadFile');
        wx.uploadFile({
          filePath: res.tempFilePaths[0],
          name: 'file',
          url: "http://localhost:8000/upload",
          formData: {
            'user': '落花人独立'
          },
          header: {
            "Content-Type": "multipart/form-data",
            'accept': 'application/json'},
          complete: (res) => {
            console.log(res);
          }
        })
        console.log('uploadFile完成');
      }
    })
  },
  /**
   * 发送点击监听
   */
  sendClick: function(e) {
    if (socketOpen) {
      console.log('test');
      // 如果打开了socket就发送数据给服务器
      sendSocketMessage(this.data.inputVal)
    }
    msgList.push({
      speaker: 'our',
      contentType: 'text',
      content: e.detail.value
    })
    inputVal = '';
    this.setData({
      msgList,
      inputVal
    });
    console.log(e);
  },

  /**
   * 退回上一页
   */
  toBackClick: function() {
    wx.navigateBack({})
  }

})
//通过 WebSocket 连接发送数据,需要先 wx.connectSocket,并在 wx.onSocketOpen 回调之后才能发送。
function sendSocketMessage(msg) {
  var that = this;
  console.log('通过 WebSocket 连接发送数据', JSON.stringify(msg))
  SocketTask.send({
    data: JSON.stringify(msg)
  }, function (res) {
    console.log('已发送', res)
  })
}

wxml:

<!--pages/consultation/consultation.wxml-->
<!-- 咨询聊天界面 -->
<view style="padding-top: 20rpx;">
  <scroll-view scroll-y scroll-into-view='{{toView}}' style='height: {{scrollHeight}};'>
    <!-- <view class='scrollMsg'> -->
    <block wx:key wx:for='{{msgList}}' wx:for-index="index">
      <!--  发出(左) -->
      <view wx:if='{{item.speaker=="others"}}' id='msg-{{index}}' class="others">
       <view>
         <image class="head-img" src='../../images/noHead.jpg'></image>
       </view>
       <view class="other-tri triangle">
         <image src='../../images/left_msg.png' mode='widthFix'></image>
       </view>
       <view class='left-msg'>{{item.content}}</view>
     </view>

  <!-- 用户发出(右) -->
  <view wx:else id='msg-{{index}}' class="our">
    <view class='right-msg'>{{item.content}}</view>
    <view class="our-tri triangle">
      <image  src='../../images/right_msg.png' mode='widthFix'></image>
    </view>
    <view>
      <image class="head-img" src='{{cusHeadIcon}}'></image>
    </view>
  </view>

</block>

<!-- 占位 -->
    <view style='width: 100%; height: 18vw;'></view>
  </scroll-view>

  <view class='input-room' style='bottom: {{inputBottom}}'>
    <image style='width: 7%; margin-left: 3.2%;' src='../../images/pic_icon.png' mode='widthFix' bindtap="upImg"></image>
    <input bindconfirm='sendClick' adjust-position='{{false}}' bindinput="getUserInput" value='{{inputVal}}' confirm-type='send' bindfocus='focus' bindblur='blur'></input>
    <button bindtap="submitTo" class='user_input_text'>发送</button>
  </view>
</view>

wxss:

page {
  background-color: #f1f1f1;
}

.others{
  display: flex; 
  padding: 2% 11% 2% 2%;
}
.head-img{
  width: 71rpx; 
  height: 71rpx; 
  border-radius: 10rpx;
}
.triangle{
  width: 4%; 
  height: 11vw; 
  display: flex; 
  align-items: center;
  z-index: 9;
}
.other-tri{
  margin-left: 0.5%; 
}
.our-tri{
  margin-right: 0.5%; 
}
.our{
  display: flex; 
  justify-content: flex-end; 
  padding: 2% 2% 2% 11%;
}
.input-room {
  width: 100%;
  height: 10%;
  border-top: 1px solid #cdcdcd;
  background-color: #f1f1f1;
  position: fixed;
  bottom: 0;
  display: flex;
  align-items: center;
  z-index: 20;
}

input {
  width: 76%;
  height: 58%;
  background-color: #fff;
  border-radius: 40rpx;
  margin-left: 2%;
  padding: 0 3%;
  font-size: 28rpx;
  color: #444;
}

.left-msg {
  font-size: 35rpx;
  color: #444;
  padding: 2.5% 2.5%;
  background-color: #fff;
  margin-left: -1%;
  border-radius: 10rpx;
  z-index: 10;
}

.right-msg {
  font-size: 35rpx;
  color: #444;
  padding: 2.5% 2.5%;
  background-color: #96EB6A;
  margin-right: -1%;
  border-radius: 10rpx;
  z-index: 10;
}
.user_input_text {
  width: 45px;
  height: 34px;
  font-size: 10px;
  line-height: 21px;
  padding-right: 18px;
  margin-right: 7px;
  border-radius: 11px;
}

If it helps you, please give me a thumbs up ~~

The whole project is not finished yet, I will put it on gitee for open source sharing!

Published 20 original articles · won praise 5 · Views 2068

Guess you like

Origin blog.csdn.net/qq_42859887/article/details/105601387