springboot整合websocket(三)上传文件(终篇)
springboot整合websocket(一)简单聊天室 springboot整合websocket(二)上传文件(引导篇) springboot整合websocket(三)上传文件(终篇)
说明
这里就涉及到一个问题,文件保存在服务器,前端页面要等后端保存完了之后,再发送下一份。
而前端想要接收服务器的消息,只能在OnMessage,所以我们需要改3个地方
- 前端webSocket.onmessage()方法里面,我们需要判断一下,哪些消息是显示的,哪些消息是发送下一份文件
- 这个采用的就是
Message类 将消息封装,转成json后发给前端,前端拿到json后在转成对象
- 前端发送文件的时候,我们需要保存文件的上传进度,以便发下一份文件的时候,我们可以发送正确的部分
- 文件切割用的是
slice() 方法,具体在js的sendFile(addSize)
- 服务器onMessage(byte[]),需要给前端发送一个消息,告诉前端发送下一部分文件
直接上代码
这里我使用了Gson,放一下依赖
<!--gson-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
服务器端
@Log4j2
@Controller
@ServerEndpoint("/websocket")
public class BaseWebsocketController
{
private final static Map<String, Session> sessions = new ConcurrentHashMap<>();
private final static Gson gson = new Gson();
@OnOpen
public void openSession(Session session, EndpointConfig config)
{
sessions.put(session.getId(), session);
sendAll("msg", "[" + session.getId() + "]进入房间");
}
@OnMessage
public void onMessage(Session session, String message) throws Exception
{
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)
{
final String filename = (String) session.getUserProperties().get("filename");
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
public void closeSession(Session session, CloseReason closeReason)
{
sessions.remove(session.getId());
System.out.println(closeReason.getReasonPhrase());
sendAll("msg", "[" + session.getId() + "]离开了房间");
}
@OnError
public void sessionError(Session session, Throwable throwable)
{
throwable.printStackTrace();
try {
session.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
private void sendAll(String tip, String message)
{
Message msg = new Message(tip, message);
final String msgStr = gson.toJson(msg);
for (Session s : sessions.values()) {
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 msg = new Message(tip, message);
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;
}
}
页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<title>websocket-demo</title>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.2.1/css/bootstrap.min.css">
</head>
<body>
<div class="container py-3">
<div class="row">
<div class="col-6">
<div>
<label for="messageArea">聊天信息:</label>
</div>
<div>
<textarea id="messageArea" readonly class="w-100" style="height: 75vh;"></textarea>
</div>
</div>
<div class="col">
<div class="my-1">
<label for="messageArea">用 户 名:</label>
</div>
<div class="my-1">
<input type="text" id="username" autocomplete="off">
</div>
<div class="my-1">
<button class="btn-info" id="joinRoomBtn">进入聊天室</button>
<button class="btn-warning" id="leaveRoomBtn">离开聊天室</button>
</div>
<hr/>
<div class="my-1">
<label for="sendMessage">输入消息:</label>
</div>
<div>
<textarea id="sendMessage" rows="5" class="w-100"></textarea>
</div>
<div class="my-1">
<button class="btn-primary" id="sendBtn">发送消息</button>
</div>
<hr/>
<div class="my-1">
<input id="file" type="file" value="选择文件"/>
<button id="fileBtn" class="btn-primary">上传</button>
</div>
</div>
</div>
</div>
<script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js"></script>
<script>
let webSocket;
let url = 'ws://127.0.0.1:8080/websocket';
let fileData = {
file: File,
filename: '',
size: 0,
uploadSize: 0,
};
let cacheSize = 4096;
$('#username').keyup(function (e) {
let keycode = e.which;
if (keycode == 13) {
$('#joinRoomBtn').click();
}
});
$('#joinRoomBtn').click(function () {
let username = $('#username').val();
webSocket = new WebSocket(url);
webSocket.onopen = function () {
console.log('webSocket连接创建。。。');
}
webSocket.onclose = function () {
$('#messageArea').append('websocket已断开\n');
}
webSocket.onmessage = function (event) {
let data = {
tip: '',
msg: ''
};
data = JSON.parse(event.data);
console.log(data)
switch (data.tip) {
case 'msg':
$('#messageArea').append(data.msg + '\n');
break;
case "file":
let addSize = parseInt(data.msg);
if (addSize > 0) {
if (!sendFile(addSize)) {
$('#messageArea').append('文件[' + fileData.filename + ']上传成功\n');
}
}
break;
}
}
webSocket.onerror = function (event) {
console.log(event)
console.log('webSocket连接异常。。。');
}
});
$('#leaveRoomBtn').click(function () {
if (webSocket) {
webSocket.close();
}
});
$('#sendBtn').click(function () {
});
$('#fileBtn').click(function () {
let files = [];
files = $('#file')[0].files;
if (files.length === 0) {
alert('请选择文件');
return;
}
webSocket.send(files[0].name);
fileData.file = files[0];
fileData.filename = files[0].name;
fileData.size = files[0].size;
fileData.uploadSize = 0;
sendFile(0);
});
function sendFile(addSize) {
fileData.uploadSize += addSize;
if (fileData.uploadSize < fileData.size) {
let dist = fileData.uploadSize + cacheSize < fileData.size ? fileData.uploadSize + cacheSize : fileData.size;
let blob = fileData.file.slice(fileData.uploadSize, dist);
let reader = new FileReader();
reader.onload = function (evt) {
let buffer = evt.target.result;
webSocket.send(buffer);
}
reader.readAsArrayBuffer(blob);
return true;
} else {
return false;
}
}
</script>
</body>
</html>
End
Question 1
Q:我又想发消息又想上传文件怎么办 A: 1、在js里面也封装一个对象,将json传给后 2、后端收到json后,转成对象,判断是发消息,还是发送的文件名就ok啦
Question 2
Q:我想选择很多个文件咋办 A:将选择的文件存进数组里面,上传完一个再上传第二个
Question 3
Q:我想同时上传多个文件欸 A: 1、websocket一次只能传递一个文件,要想多个文件,只能建立多个websocket连接。
|