简体   繁体   English

如何使用 WebSockets 将多个文件发送到服务器?

[英]How to send multiple files to server using WebSockets?

I'm trying to send multiple files from the client to the NodeJS server using WebSockets.我正在尝试使用 WebSockets 将多个文件从客户端发送到 NodeJS 服务器。

To send one file, I currently do the following:要发送一个文件,我目前执行以下操作:

// Client

let upload = document.getElementById('upload')

button.onclick = async function() {
    let file = upload.files[0];
    let byteFile = await getAsByteArray(file);
    socket.send(byteFile);
}

async function getAsByteArray(file) {
  return new Uint8Array(await readFile(file))
}

function readFile(file) {
    return new Promise((resolve, reject) => {
        let reader = new FileReader()

        reader.addEventListener("loadend", e => resolve(e.target.result))
        reader.addEventListener("error", reject)

        reader.readAsArrayBuffer(file)
    });
}
// Server

ws.on('message', function incoming(message) {

    // This returns a buffer which is what I'm looking for when working with a single file.
    console.log(message);
    return;
}

This works great for one file.这适用于一个文件。 I'm able to use the buffer and process the file as I would like.我可以根据需要使用缓冲区并处理文件。 To send two files, my thought was to convert each file to a Uint8Array (as I did for the single file) and push to an array like so:要发送两个文件,我的想法是将每个文件转换为 Uint8Array(就像我对单个文件所做的那样)并像这样推送到数组:

// Client

let filesArray = [];
let files = upload.files;       // Grab uploaded Manifests

for (let file of files) {
    let byteFile = await getAsByteArray(file);
    filesArray.push(byteFile);
}

socket.send(filesArray);

In the same way as with one file, the server returns a buffer for the array that was sent;与处理一个文件的方式相同,服务器为发送的数组返回一个缓冲区; however, I'm not sure how to work with it.但是,我不确定如何使用它。 I need each file to be their own buffer in order to work with them.我需要每个文件成为它们自己的缓冲区才能使用它们。 Am I taking the wrong approach here?我在这里采取了错误的方法吗? Or am I just missing some conversion to be able to work with each file?或者我只是错过了一些能够处理每个文件的转换?

Send each buffer in separate message:在单独的消息中发送每个缓冲区:

button.onclick = async function() {
    upload.files.forEach(file => socket.send(await getAsByteArray(file)));
}

This works great for one file.这适用于一个文件。

Not really.并不真地。 Unless it is supposed to be used in some very simplistic setup, probably in an isolated (from the internet) network.除非它应该用于一些非常简单的设置,可能是在一个隔离的(来自互联网的)网络中。

You literally send a sequence of bytes to the server which reads it and what is it going to do with it?您实际上将一个字节序列发送到读取它的服务器,它将如何处理它? Save it to disk?保存到磁盘? Without validating?不验证? But how can it validate a random sequence of bytes, it has no hint about what it is?但是它如何验证随机的字节序列,它没有暗示它是什么? Secondly, where will it save it?其次,它将保存在哪里? Under what name?以什么名义? You didn't send any metadata like filename.您没有发送任何元数据,例如文件名。 Is it supposed to generate a random name for it?它应该为它生成一个随机名称吗? How will the user know that this is his file?用户如何知道这是他的文件? Finally, what about security?最后,安全性如何? Can I open a WebSocket connection to your server and spam it with arbitrary sequences of data, effictively killing it?我可以打开与您的服务器的 WebSocket 连接并使用任意数据序列向其发送垃圾邮件,从而有效地杀死它吗? You probably need some authentication, but even with it, can any user spam such upload?您可能需要一些身份验证,但即使有了它,任何用户都可以发送这样的上传垃圾邮件吗? Maybe you additionally need tokens with timeouts for that (but then you have to think about how will your server issue such tokens).也许您还需要带有超时的令牌(但是您必须考虑您的服务器将如何发布此类令牌)。

I need each file to be their own buffer in order to work with them.我需要每个文件成为它们自己的缓冲区才能使用它们。

No, you don't.不,你没有。 The bare minimum you need is (1) the ability to send files with metadata from the client and (2) the ability to read files with metadata on the server side.您需要的最低要求是(1)能够从客户端发送带有元数据的文件,以及(2)能够在服务器端读取带有元数据的文件。 You most likely need some authentication mechanism as well.您很可能还需要一些身份验证机制。 Typically you would use classical HTTP for that, which I strongly encourage you to utilize.通常,您会为此使用经典的 HTTP,我强烈建议您使用它。

If you want to stick with WebSockets, then you have to implement those already well established mechanisms by yourself.如果你想坚持使用 WebSockets,那么你必须自己实现那些已经很完善的机制。 So here's how I would do that:所以我会这样做:

(1) Define a custom protocol on top of WebSocket. (1) 在 WebSocket 之上定义一个自定义协议。 Each frame should have a structure, for example first two bytes indicating "size of command", next X bytes (previous 2 bytes interpreted as int of size 16) the command as string.每个帧都应该有一个结构,例如前两个字节表示“命令大小”,接下来的 X 个字节(前 2 个字节解释为大小为 16 的 int)命令为字符串。 On the server side you read that command, map it to some handler, and run appropriate action.在服务器端,您读取该命令,map 将其发送到某个处理程序,并运行适当的操作。 The data that the command should process, is the data from the remaining bytes of the frame.命令应该处理的数据是来自帧剩余字节的数据。

(2) Setup authentication. (2)设置认证。 Not in the scope of this answer, just indicating it is crucial.不在此答案的 scope 中,只是表明它至关重要。 I'm putting this after (1) because you can reuse the protocol for that.我把它放在 (1) 之后,因为您可以为此重用协议。

(3) Whenever you want to upload a file: send a command "SEND" to the server. (3) 每当您要上传文件时:向服务器发送命令“SEND”。 In the same frame, after "SEND" command put metadata (file name, size, content type, etc.), you can encode it as JSON prefixed with length.在同一帧中,在“SEND”命令放置元数据(文件名、大小、内容类型等)之后,可以将其编码为 JSON 并以长度为前缀。 Afterwards put the content of the file in the buffer.然后将文件的内容放入缓冲区。

This solution should obviously be refined with (mentioned earlier) tokens.这个解决方案显然应该使用(前面提到的)令牌进行改进。 For proper responsivness and concurrency, you should probably split large files into separate WebSocket frames (which complicates the design a lot).为了获得适当的响应性和并发性,您可能应该将大文件拆分为单独的 WebSocket 帧(这会使设计复杂化很多)。

Anyway, as you can see, the topic is far from trivial and requires lots of experience.无论如何,正如您所看到的,这个话题远非微不足道,需要大量经验。 And it is basically reimplementing what HTTP does anyway.它基本上是在重新实现 HTTP 所做的事情。 Again: I strongly suggest you use plain old HTTP.再说一遍:我强烈建议您使用普通的旧 HTTP。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM