由于安卓资源管理器展示的路径不尽相同,各种软件保存文件的位置也不一定一样.对于普通用户上传文件时,查找文件可能是一个麻烦的事情.后来想到了一个办法,使用pc端进行辅助上传.
文章目录
实现思路
- pc端与服务器建立websocket连接;
- 服务器将sessionId传递到pc端;
- pc端生成二维码;
- 手机端扫描二维码,读取pc端sessionId;
- 手机端与服务器建立websocket连接;
- 手机端将fileId(后面再解释)、pc端sessionId、token等参数传递给服务器;
- 服务器更新pc端session 对应的fileId;
- 服务器将fileId、token等发送到pc端;
- pc使用token、fileId等请求文件列表并进行展示;
- 手机端、pc端进行文件修改后,向服务器发送给更新信号,服务器将更新信号转发到对端。
1.0 实现
定义web与客户端通信数据类型和数据格式
- 定义web与客户端通信数据类型
public class MsgType {
public static final int UPDATE = 0; //提示客户端数据发生更新
public static final int REQ = 1; //发送/接受fileId等字段
public static final int SELF = 3; //建立连接后,web端发送client其sessionId
public static final int ERR_SESSION = 4; //提示session不存在或已close
public static final int HEART_BEAT = 100; //心跳包
}
- 定义web与客户端通信数据格式
@Data
public class MsgData {
private int type; //对应 MsgType
private String sessionId; //SELF 对应自身sessionId; REQ 对应pc端sessionId;
private String fileId; //建立连接后,向pc端发送fileId等字段
web端websocket实现
创建spring-boot项目,添加web\websocket相关依赖
使用maven引入websocket依赖;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置websocket和访问路径的映射关系
@Configuration //配置websocket和访问路径的映射关系
@EnableWebSocket // 全局开启WebSocket支持
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebSocketServer(), "/websocket").setAllowedOrigins("*");
}
}
web端对客户端数据的管理
- 定义web管理session的数据结构
@Data
public class SessionData {
private int sessionType; // 1 master(app). 0 pc
private String fileId; //pc会话ID
private WebSocketSession session;
private String sessionId;
//虽然可以通过session.getId()获取到sessionId,但session关闭后,读取就会报错
web端对session的管理逻辑
-
新创建的连接添加到链表上,web向客户端发送SELF,告知其对应的sessionId;
-
断开连接时,如果是pc端session直接从链表中删除,如果是app端session,将其他相同fileId的session全部关闭并从链表删除;
-
接收到新消息后,根据消息类型进行分类处理:
- 心跳包,则直接返回;
- REQ app发送的fileId\pc端sessionId等字段,修改sessions上app连接和pc端SessionData内的fileId字段;
并将fileId等字段发送给pc端; - UPDATE 给所有相同fileId的session发送更新信号;
注意: sessions遍历\删除\添加必须添加synchronized,否则ConcurrentModificationException
package com.example.im.ws;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* @ClassName WebSocketServer
* @Description 处理websocket 连接
* @Author guchuanhang
* @date 2025/1/25 14:01
* @Version 1.0
**/
@Slf4j
public class WebSocketServer extends TextWebSocketHandler {
private final Object syncObject = new Object();
private final List<SessionData> sessions =
new ArrayList<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("New connection established: " + session.getId());
SessionData sessionData = new SessionData(session);
synchronized (syncObject) {
sessions.add(sessionData);
}
MsgData msgData = new MsgData();
msgData.setType(MsgType.SELF);
msgData.setSessionId(session.getId());
session.sendMessage(new TextMessage(new Gson().toJson(msgData)));
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
String payload = message.getPayload();
log.info("handleTextMessage: " + session.getId());
log.info("Received message: " + payload);
final MsgData msgData = new Gson().fromJson(payload, MsgData.class);
//master 发来的需求.
switch (msgData.getType()) {
case MsgType.HEART_BEAT: {
//heart beat
break;
}
case MsgType.REQ: {
//set master
{
SessionData sessionData = null;
synchronized (syncObject) {
final Optional<SessionData> any = sessions.stream().
filter(s -> s.getSessionId()