springboot整合websocket(一)简单聊天室
springboot整合websocket(二)上传文件(引导篇)
springboot整合websocket(三)上传文件(终篇)
说明
这里就涉及到一个问题,文件保存在服务器,前端页面要等后端保存完了之后,再发送下一份。
而前端想要接收服务器的消息,只能在OnMessage,所以我们需要改3个地方
- 前端webSocket.onmessage()方法里面,我们需要判断一下,哪些消息是显示的,哪些消息是发送下一份文件
- 这个采用的就是Message类将消息封装,转成json后发给前端,前端拿到json后在转成对象
- 前端发送文件的时候,我们需要保存文件的上传进度,以便发下一份文件的时候,我们可以发送正确的部分
- 文件切割用的是slice()方法,具体在js的sendFile(addSize)
- 服务器onMessage(byte[]),需要给前端发送一个消息,告诉前端发送下一部分文件
直接上代码
这里我使用了Gson,放一下依赖
服务器端com.google.code.gson gson
@Log4j2
@Controller
@ServerEndpoint("/websocket")
public class baseWebsocketController
{
//使用 ConcurrentHashMap, 保证线程安全, static全局共享 session
//这里之所以static,是因为这个类不是单例的!!
//他虽然有@Controller注解,但是不适用Ioc容器中拿对象,每一次请求过来都是一个新的对象
//存放 session
private final static Map sessions = new ConcurrentHashMap<>();
private final static Gson gson = new Gson();
//onopen 在连接创建(用户进入聊天室)时触发
@OnOpen
public void openSession(Session session, EndpointConfig config)
{
//将session存起来, 用于服务器向浏览器发送消息
sessions.put(session.getId(), session);
sendAll("msg", "[" + session.getId() + "]进入房间");
}
//响应字符串
@OnMessage
public void onMessage(Session session, String message) throws Exception
{
// session.getUserProperties() 是一个 Map, 用于存放数据
session.getUserProperties().put("filename", message);
//这里处理一下, 如果有该文件, 就先删除
File file = new File(getProjectRootPath(), message);
if (file.exists()) {
file.delete();
}
//输出一下文件路径
System.out.println(file.getCanonicalPath());
}
//响应字节流
@OnMessage
public void onMessage(Session session, byte[] message)
{
// 1、先拿到文件名
final String filename = (String) session.getUserProperties().get("filename");
// 2、追加到该文件
File file = new File(getProjectRootPath(), filename);
try (OutputStream os = new FileOutputStream(file, true)) {
os.write(message, 0, message.length);
}
catch (IOException e) {
e.printStackTrace();
}
//返回已经上传的字节数
send(session, "file", message.length + "");
}
//onclose 在连接断开(用户离开聊天室)时触发
@OnClose
public void closeSession(Session session, CloseReason closeReason)
{
//记得移除相对应的session
sessions.remove(session.getId());
//打印一下原因
System.out.println(closeReason.getReasonPhrase());
sendAll("msg", "[" + session.getId() + "]离开了房间");
}
@OnError
public void sessionError(Session session, Throwable throwable)
{
throwable.printStackTrace();
//通常有异常会关闭session
try {
session.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
private void sendAll(String tip, String message)
{
//使用Message封装
Message msg = new Message(tip, message);
//用gson转成json字符串
final String msgStr = gson.toJson(msg);
for (Session s : sessions.values()) {
//获得session发送消息的对象
//Basic是同步, 会阻塞
//Async是异步, 这个会有多线程并发导致异常, 发送消息太快也会有并发异常, 需要有 消息队列 来辅助使用
final RemoteEndpoint.Basic remote = s.getBasicRemote();
try {
//发送消息
remote.sendText(msgStr);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
private void send(Session session, String tip, String message)
{
//使用Message封装
Message msg = new Message(tip, message);
//用gson转成json字符串
final String msgStr = gson.toJson(msg);
final RemoteEndpoint.Basic remote = session.getBasicRemote();
try {
//发送消息
remote.sendText(msgStr);
}
catch (IOException e) {
e.printStackTrace();
}
}
private String getProjectRootPath()
{
try {
String path = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath();
path = URLDecoder.decode(path, "UTF-8");
final File file = new File(path, "static");
return file.getCanonicalPath();
}
catch (Exception e) {
e.printStackTrace();
return "";
}
}
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
private static class Message
{
private String tip;
private String msg;
}
}
页面
websocket-demo
End
Question 1
Q:我又想发消息又想上传文件怎么办
A:
1、在js里面也封装一个对象,将json传给后
2、后端收到json后,转成对象,判断是发消息,还是发送的文件名就ok啦
Q:我想选择很多个文件咋办
A:将选择的文件存进数组里面,上传完一个再上传第二个
Q:我想同时上传多个文件欸
A:
1、websocket一次只能传递一个文件,要想多个文件,只能建立多个websocket连接。



