【问题标题】:Send file thought Actix websocket通过 Actix websocket 发送文件
【发布时间】:2021-06-10 22:05:45
【问题描述】:

为了通过 websocket 发送文件,我很难找到文档或教程。 这是我的 JS:

['dragleave', 'drop'].forEach(event_name =>
document.addEventListener(event_name, function(e) {
  e.preventDefault();
  let files = e.dataTransfer.files;
  var reader = new FileReader();
  for(let i=0;i<files.length; i++) {
    reader.onload = function(event) {
      socket.send(event.target.result);
    };
    reader.readAsArrayBuffer(files[i]);
  }
  }, false));

我的 rust 代码是基于官方的例子(https://github.com/actix/examples/tree/master/websockets/websocket):

impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for RatioUpWS {
    fn handle(&mut self, msg: Result<ws::Message, ws::ProtocolError>, ctx: &mut Self::Context,) {
        // process websocket messages
        println!("WS: {:?}", msg);
        match msg {
            Ok(ws::Message::Ping(msg)) => {
                self.hb = Instant::now();
                ctx.pong(&msg);
            }
            Ok(ws::Message::Pong(_)) => {self.hb = Instant::now();}
            Ok(ws::Message::Text(text)) => {
                ctx.text(text);
            }
            Ok(ws::Message::Binary(bin)) => {
                let mut pos = 0;
                let mut buffer = File::create("foo.txt").unwrap();  // notice the name of the file that will be written
                while pos < bin.len() {
                    let bytes_written = buffer.write(&bin[pos..]).unwrap();
                    pos += bytes_written
                };
                ctx.binary(bin)
            },
            Ok(ws::Message::Close(reason)) => {
                ctx.close(reason);
                ctx.stop();
            }
            _ => ctx.stop(), //if file>64K, it goes here
        }
    }
}

它似乎被识别为短信,我不知道如何反序列化它。 actix_web::web::Form&lt;FormDataStruct&gt; 好像不太合适。

有什么想法吗?

【问题讨论】:

    标签: websocket rust file-transfer rust-actix


    【解决方案1】:

    您的 WebSocket 服务器将消息识别为文本,因为您的 JavaScript 代码使用 FormData() 而不是使用 ArrayBuffer 发送文本数据。

    要使用 WebSocket 从浏览器传输文件,您可以使用以下示例代码(注意 WebSocket 声明和 sendFile() 函数):

        var ws;
        function connectToWS() {
          var wsUri = (window.location.protocol=='https:'&&'wss://'||'ws://')+window.location.host + '/ws/';
          console.log(wsUri);
          ws = new WebSocket(wsUri);
          ws.binaryType = "arraybuffer";  // Note the binaryType here
          ws.onopen = function() {alert("Connected.");};
          ws.onmessage = function(evt) {alert(evt.msg);};
          ws.onclose = function() {
            ws = null;
            alert("Connection is closed...");
          };
          ws.onerror = function(e) {alert(e.msg);}
        }
    
        function sendFile() {
          var file = document.getElementById('filename').files[0];
          var reader = new FileReader();
          var rawData = new ArrayBuffer();            
          reader.loadend = function() {}
          reader.onload = function(e) {
              rawData = e.target.result;
              ws.send(rawData);
              alert("the File has been transferred.")
          }
          reader.readAsArrayBuffer(file);
        }
    

    要在服务器端处理传输的字节,您可以使用标准 Rust 库 (std::io::Write)。更改您提供的相同示例 Rust 代码以使用 std::io 处理 .txt 文件:

    impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for MyWebSocket {
        fn handle(
            &mut self,
            msg: Result<ws::Message, ws::ProtocolError>,
            ctx: &mut Self::Context,
        ) {
            // process websocket messages
            println!("WS: {:?}", msg);
            match msg {
                Ok(ws::Message::Ping(msg)) => {
                    self.hb = Instant::now();
                    ctx.pong(&msg);
                }
                Ok(ws::Message::Pong(_)) => {
                    self.hb = Instant::now();
                }
                Ok(ws::Message::Text(text)) => ctx.text(text),
                Ok(ws::Message::Binary(bin)) => {
                    let mut pos = 0;
                    let mut buffer = File::create("foo.txt").unwrap();  // notice the name of the file that will be written
                    while pos < bin.len() {
                        let bytes_written = buffer.write(&bin[pos..]).unwrap();
                        pos += bytes_written
                    };
                    ctx.binary(bin)
                },
                Ok(ws::Message::Close(reason)) => {
                    ctx.close(reason);
                    ctx.stop();
                }
                _ => ctx.stop(),
            }
        }
    }
    

    您可以在此 repo 中查看完整的工作示例:https://github.com/vnderlev/actix_ws_file_transfer(改编自 official actix/example repository)。

    希望这个回答对你有帮助。

    【讨论】:

    • 我现在能够以二进制格式传输并写入小文件(我认为
    猜你喜欢
    • 2017-12-09
    • 1970-01-01
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 2012-07-03
    • 1970-01-01
    • 2013-05-14
    相关资源
    最近更新 更多