Wechat mini sala de chat del programa + websocket + carga de archivos (enviar fotos)

Recientemente, los amigos están escribiendo applets de WeChat. Una de las necesidades es construir una sala de chat, que pueda chatear con varias personas, chatear en privado y enviar imágenes. Sin embargo, debido a que no tenía ningún conocimiento relevante en esta área, lentamente lo miré y lo hice. Fue muy difícil en la etapa inicial, y el camino no fue fácil.
Veamos primero el efecto:
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
Inserte la descripción de la imagen aquí
1. El paquete websocket está construido,
lo construí con Springboot + gradle .

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'
}

Luego proporcione primero el código websocket de java.

	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--;
		}
	}

Applet WeChat

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;
}

Si te ayuda, por favor dame un visto bueno ~~

¡Todo el proyecto aún no está terminado, lo pondré en gitee para compartir código abierto!

Publicado 20 artículos originales · ganado elogios 5 · Vistas 2068

Supongo que te gusta

Origin blog.csdn.net/qq_42859887/article/details/105601387
Recomendado
Clasificación