springboot整合websocket(三)上传文件(引导篇)

springboot整合websocket(三)上传文件(引导篇)

springboot整合websocket(一)简单聊天室
springboot整合websocket(二)聊天室补充篇

springboot整合websocket(三)上传文件(引导篇)
springboot整合websocket(四)上传文件(终篇)


Gitee 地址

gitee是更早之前的版本,只有聊天室的功能,没有文件上传的功能,核心不变,具体实现上有所出入

https://gitee.com/chr_demo/web-socket

如果大家能够有写好的可以共享一下嘛,我实在不想动了(//懒死\\)


其实,上传文件通常会调用另一个接口,而不是通过websocket,但毕竟人家支持这么做,所以还是写下。
还挺麻烦的,分两章来写。

这一章我们先做个初版,会有bug,下一章会在这一章基础上修复这个bug。
毕竟真的有很多东西啊!!

一、回顾一下

上次提到有一个方法:

void onMessage(Session session, byte[] message);

这个是用来上传文件用的,先解释一下 byte[] 这个参数。
好吧~ 大家猜的没错,这个就是我们文件的二进制流

所以说,我们要干两件事
第一,我们js要获得文件,并转成二进制流发过来
第二,在上传之前,我们需要知道文件的名字,因为这个二进制流是一个数组,没法知道文件的名字!(实测,js不能修改这个二进制流哦)

二、先做后端吧

说一下重点
1、在 onMessage(string) 中用将文件信息存起来,并删除已有的文件
2、在onMessage(byte[])保存文件
3、在onClose中将关闭原因打印一下(这就是那个bug导致的)。

@Log4j2
@Controller
@ServerEndpoint("/websocket")
public class BaseWebsocketController
{

    //使用 ConcurrentHashMap, 保证线程安全, static全局共享 session

    //这里之所以static,是因为这个类不是单例的!!
    //他虽然有@Controller注解,但是不适用Ioc容器中拿对象,每一次请求过来都是一个新的对象

    //存放 session
    private final static Map<String, Session> sessions = new ConcurrentHashMap<>();

    //onopen 在连接创建(用户进入聊天室)时触发
    @OnOpen
    public void openSession(Session session, EndpointConfig config)
    {

    }

    //响应字符串
    @OnMessage
    public void onMessage(Session session, String message)
    {
        //使用 fastjson 解析 json 字符串
        final Message data = JSONObject.parseObject(message, Message.class);
        //响应的信息
        final Message response = Message.builder()
                .operation(data.getOperation())         //将请求的 operation 放入
                .build();
        //根据不同的 operation 执行不同的操作
        switch (data.getOperation()) {
            //进入聊天室后保存用户名
            case "tip":
                session.getUserProperties().put("username", data.getMsg());
                sessions.put(session.getId(), session);
                response.setMsg("[" + data.getMsg() + "]进入房间");
                sendAll(JSONObject.toJSONString(response));
                break;
            //发送消息
            case "msg":
                final String username = (String) session.getUserProperties().get("username");
                response.setMsg("[" + username + "]" + data.getMsg());
                sendAll(JSONObject.toJSONString(response));
                break;
            case "filename":
                //删除原有文件
                File file = new File(SpringbootPathUtil.getResourcePath() + "/web-socket/file/" + data.getMsg());
                file.delete();

                //保存文件信息
                session.getUserProperties().put("file", file);

                response.setMsg("文件【" + data.getMsg() + "】开始上传");
                sendTo(session, JSONObject.toJSONString(response));
                break;
        }
    }

    //响应字节流
    @OnMessage
    public void onMessage(Session session, byte[] message) throws Exception
    {
        final Message response = new Message();

        final String username = (String) session.getUserProperties().get("username");
        final File file = (File) session.getUserProperties().get("file");
        log.info(file.getCanonicalPath());

        if (saveFile(file, message)) {
            response.setOperation("file-upload-success");
            response.setMsg("[" + username + "]上传了一个文件【" + file.getName() + "】");
            sendAll(JSONObject.toJSONString(response));
        }
        else {
            response.setOperation("file-upload-fail");
            response.setMsg("文件【" + file.getName() + "】上传失败");
            sendTo(session, JSONObject.toJSONString(response));
        }
    }

    //onclose 在连接断开(用户离开聊天室)时触发
    @OnClose
    public void closeSession(Session session, CloseReason closeReason)
    {
        System.out.println(closeReason.toString());
        //记得移除相对应的session
        sessions.remove(session.getId());

        sendAll("[" + session.getId() + "]离开了房间");
    }

    @OnError
    public void sessionError(Session session, Throwable throwable)
    {
        //通常有异常会关闭session
        try {
            session.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void sendAll(String message)
    {
        for (Session s : sessions.values()) {
            sendTo(s, message);
        }
    }

    private void sendTo(Session session, String message)
    {
        final RemoteEndpoint.Basic remote = session.getBasicRemote();
        try {
            //发送消息
            remote.sendText(message);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean saveFile(File file, byte[] message)
    {
        try (OutputStream os = new FileOutputStream(file)) {
            os.write(message, 0, message.length);
            return true;
        }
        catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }
}

三、页面

1、html

说一下重点
  1. 先通过 sendMessage(‘filename’, file.name); 将文件信息发给服务器,让服务器保存
  2. 服务其保存好信息后,再发送文件。
    function sendFile(file){…}
  3. 发送文件的时候,要使用 FileReader() 对象将文件转成二进制流
<!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" style="max-height: 50vh"></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;
    //ip和端口号用自己项目的
    //{websocket}: 其实是刚刚那个@ServerEndpoint("/websocket")中定义的
    let url = 'ws://127.0.0.1:8080/websocket';
    
    let file;
    
    $('#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连接创建。。。');
        sendMessage('tip', username);
      }
      webSocket.onclose = function () {
        console.log('webSocket已断开。。。');
        $('#messageArea').append('websocket已断开\n');
      }
      webSocket.onmessage = function (event) {
        //这个 data 和刚刚的 Message 一样
        let data = {
          operation: '',
          msg: ''
        };
        
        data = JSON.parse(event.data);
        switch (data.operation) {
          case "tip":
            $('#messageArea').append(data.msg + '\n');
            break;
          case "msg":     //显示消息
            $('#messageArea').append(data.msg + '\n');
            break;
          case "filename":
            $('#messageArea').append(data.msg + '\n');
            sendFile(file);
            break;
          case "file-upload-success":
            $('#messageArea').append(data.msg + '\n');
            break;
          case "file-upload-fail":
            $('#messageArea').append(data.msg + '\n');
            break;
        }
      }
      webSocket.onerror = function (event) {
        console.log(event)
        console.log('webSocket连接异常。。。');
      }
    });
    
    //退出聊天室
    $('#leaveRoomBtn').click(function () {
      if (webSocket) {
        //关闭连接
        webSocket.close();
      }
    });
    
    //发送消息
    $('#sendBtn').click(function () {
      var msg = $('#sendMessage').val();
      if (msg.trim().length === 0) {
        alert('请输入内容');
        return;
      }
      sendMessage('msg', $('#sendMessage').val());
      
      $('#sendMessage').val('');
    });
    
    //上传文件
    // https://www.cnblogs.com/myfjd/archive/2012/03/22/2411374.html
    $('#fileBtn').click(function () {
      let files = [];
      files = $('#file')[0].files;
      if (files.length === 0) {
        alert('请选择文件');
        return;
      }
      
      //发送文件名
      file = files[0];
      sendMessage('filename', file.name);
      
    });
    
    //发送消息
    function sendMessage(operation, msg) {
      //这个 data 和刚刚的 Message 一样
      let data = {
        operation: operation,
        msg: msg,
      };
      
      //将 data 转成 json 字符串
      webSocket.send(JSON.stringify(data));
    }
    
    //发送文件
    function sendFile(file){
      //挖个坑先
      let blob = file;
      //文件读取对象
      let reader = new FileReader();
      
      //文件加载后的回调函数
      reader.onload = ev => {
        let buffer = ev.target.result;
        webSocket.send(buffer);
      }
      //二进制加载文件
      reader.readAsArrayBuffer(blob);
    }
  
  </script>

</body>
</html>

2、 接下来,大家就可以开心的上传文件了

这时候,有的同学就成功了,有的同学就失败了。
成功的同学选个大点的文件(>10M)
失败的同学选个小点的文件(<1M)

失败的同学会显示websocket已断开,如下图
在这里插入图片描述
看一下后台的报错:(检查一下onClose是不是和我上面的一样,要打印一下关闭原因哦~)

No async message support and buffer too small. Buffer size: [8,192], Message size: [2,254,618]

这个意思是说-------->
文件:我有2,254,618B,我的很大,你忍一下
Websocket:不行,我只有8192B,太大了,忍不了,放不开

所以啊,我们要将文件切分,挨个上传,一次传一点,这篇有点长,我们下篇再说

End


springboot整合websocket(一)简单聊天室
springboot整合websocket(二)聊天室补充篇

springboot整合websocket(三)上传文件(引导篇)
springboot整合websocket(四)上传文件(终篇)

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
我们很高兴能回答您的问题。要使用Spring Boot整合WebSocket进行文件推送,您可以按照以下步骤操作: 1. 引入WebSocket依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 配置WebSocket配置类 创建WebSocket配置类,用于配置WebSocket相应的参数: ``` @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/ws").setAllowedOrigins("*"); } @Bean public WebSocketHandler myHandler() { return new MyHandler(); } } ``` 3. 编写WebSocket处理器 创建一个WebSocketHandler的实现类,用于处理WebSocket连接和消息的输: ``` public class MyHandler implements WebSocketHandler { @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { System.out.println("Connection established: " + session); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception { if (message instanceof BinaryMessage) { // 接收二进制文件,并将其保存到指定路径 BinaryMessage binaryMessage = (BinaryMessage) message; byte[] data = binaryMessage.getPayload().array(); FileOutputStream fos = new FileOutputStream("/path/to/save/file"); fos.write(data); fos.close(); } } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { System.out.println("Transport error: " + exception); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { System.out.println("Connection closed: " + closeStatus); } @Override public boolean supportsPartialMessages() { return false; } } ``` 4. 前端连接WebSocket 在前端代码中连接WebSocket,用于向服务端发送文件: ``` var ws = new WebSocket("ws://localhost:8080/ws"); function sendFile(file) { var reader = new FileReader(); reader.onload = function(e) { var arrayBuffer = e.target.result; var binaryMessage = new Blob([arrayBuffer], {type: file.type}); ws.send(binaryMessage); }; reader.readAsArrayBuffer(file); } ``` 注:上述代码仅作为示例,请根据具体需要进行修改。 希望这个答案可以帮助到您!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值