如何使用ArrayBuffer将JSON对象发送到websocket?

问题描述 投票:5回答:1

我试图简单地将文件上传到node.js服务器。

为此,我使用文件API和readAsArrayBuffer。这是在触发输入文件“change”事件时调用的代码,以及一些hepler函数(我使用COBY库进行套接字发送和其他事件设置,binaryType设置为arraybuffer):

COBY.events = {
  "file": (e) => {
       files = Array.from(e.target.files);
       startReadingFile(files[0]);
   }
};

function startReadingFile(file) {
   readFileFrom(file, 0, chunkSize, (array, r) => {
       COBY.socketSend({"start uploading a file": {
           name:file.name,
           type:file.type,
           size:file.size,
           data:(array)
       }});
       console.log("didnt something?", r, Array.from(r));
   });
}

function readFileFrom(file, start, end, callback) {
   var sliced = file.slice(start, end);
   var reader = new FileReader();
   reader.onload = (event) => {
       result = (event.target.result);
       var arr = Array.from(new Uint8Array(result));
       if(callback && callback.constructor == Function) {
           currentPosition = end;
           callback(arr, result);
       }
   }
   reader.readAsArrayBuffer(sliced);
}

在我的服务器上(我使用的是coby-node库,它是COBY客户端库的node.js版本):

var coby = require("coby-node");
var fs = require("fs");
var files = {};
var kilobyte = 1024;

function makeBigFile(name, number) {
    var test = fs.createWriteStream("./" + name, {flags: "w+"});
    console.log("OK?",name);
    [...Array(number)].forEach((x, i) => test.write(i+"\n"));
}

//makeBigFile("OKthere.txt", 12356);
coby.startAdanServer({
    onOpen:(cs) => {
        console.log("something just connected! Let's send it something");
     //   cs.send({"Whoa man !":1234});
        cs.send({asdf :3456789});
    },

    onAdanMessage: (cs, msg) => {
     //   console.log("HMM weird just got this message...", msg);
    },

    adanFunctions: {
        "do something important": (cs, data) => {
            console.log("I just got some message:", data);
            console.log(cs.server.broadcast);
            cs.server.broadcast({"look out":"here I am"}, {
                current: cs
            });

            cs.send({message:"OK I did it I think"});
        },
        "start uploading a file": (cs, data) => {
            if(data.data && data.data.constructor == Array) {
                var name = data["name"]
                files[name] = {
                    totalSize:data.size,
                    downloadedSize:0
                };

                files[name]["handler"] = fs.createWriteStream("./" + data.name, {
                    flags: "w+"
                });

                files[name]["handler"].on("error", (err) => {
                    console.log("OY vay", err);
                });
                cs.send({"ok dude I need more": {
                    name:name,
                    bytePositionToWriteTo:0,
                    totalLength:files[name]["totalSize"]
                }});
            }
        },
        "continue uploading file": (cs, data) => {

            var name = data.name;
            if(files[name]) {
                var handler = files[name]["handler"];

                var uint = Uint8Array.from(data.bufferArray);
                var myBuffer = Buffer.from(uint.buffer);
                var start = data.startPosition || 0,
                    end = myBuffer.byteLength + start;

                files[name].downloadedSize += myBuffer.byteLength;


                if(files[name].downloadedSize < files[name]["totalSize"]) {

                    cs.send({"ok dude I need more": {
                        name:name,
                        bytePositionToWriteTo:files[name].downloadedSize,
                        totalLength:files[name]["totalSize"]
                    }});
                    try {
                        handler.write(myBuffer);
                    } catch(e) {
                        console.log("writing error: ", e);
                    }
                } else {
                    end = files[name]["totalSize"];
                    handler.write(myBuffer);
                    console.log("finished, I think?");
                    console.log(files[name].downloadedSize, "total: ", files[name]["totalSize"]);
                    console.log("   start: ", start, "end: ", end);
                }


            }
        }
    },
    intervalLength:1000
});

function startUnity() {
    coby.cmd(`./MyUnity/Editor/Unity.exe -batchmode -quit -projectPath "./MyUnity/totally empty" -executeMethod COBY.Start -logfile ./new123folder/wow.txt`, {
        onData:(data) => {
            console.log(data);
        },
        onError:(data) => {
            console.log(data);
        },
        onExit:(exitCode) => {
            console.log("exitted with code: " + exitCode);
        },
        onFail:(msg) => {
            console.log(msg);
        }
    });  
}

到目前为止,这个实际上传文件,你可以用npm install coby-node测试它,但它花了很多时间因为我是JSON.stringifing一个Array.from(new Uint8Array(/* the ArrayBuffer result */))然后在服务器端我重新JSON解析它,但我怎么做只是将实际的ArrayBuffer发送到websocket?我想发送arraybuffer以及文件名和其他数据,所以我想将它包含在JSON对象中,但是当我JSON.stringify(/一个ArrayBuffer /)时,结果总是[],而IDK如何用我自己的数据发送一个ArrayBuffer ???

另外,似乎需要花费大量时间使用Array.from(new Uint8Array(arrayBufer))你认为readAsDataURL会更快吗?

我能够,顺便说一下,ITSELF通过websocket发送一个带有binayType =“arraybuffer”的arraybuffer,但是如何用它包含文件名?

javascript arrays node.js json fileapi
1个回答
0
投票

所以你想发送结构化的二进制数据。大多数通用二进制格式使用类型长度值编码(ASN.1Nimn是很好的例子)。

在您的情况下,您可能需要一个更简单的方案,因为您有固定的字段:“名称”,“类型”,“大小”,“数据”。你已经知道他们的类型。所以你可以得到长度值。我们的想法是,字节流中的每个字段都以包含值长度的一个或两个字节开头。因此,解析器将知道在下一个值之前要读取的字节数,从而无需分隔符。

假设您要对此进行编码:

{
  name: "file.txt",
  type: "text/plain",
  size: 4834,
  data: <an ArrayBuffer of length 4834>
}

“size”字段实际上是有用的,因为所有其他长度适合单个字节但内容长度不适合。

所以你用字节创建一个新的ArrayBuffer:

08 (length of the file name)
66 69 6c 65 2e 74 78 74 (the string "file.txt")
0a (length of the content type)
74 65 78 74 2f 70 6c 61 69 6e (the string "text/plain")
02 (you need two bytes to represent the size)
12 e2 (the size, 4834 as an unsigned int16)
... and finally the bytes of the content

使用客户端JavaScript执行此操作仅比使用node.js缓冲区稍微困难一些。首先,您需要计算您需要发送的ArrayBuffer的总长度。

// this gives you how many bytes are needed to represent the size
let sizeLength = 1
if (file.size > 0xffff)
  sizeLength = 4
else if (file.size > 0xff)
  sizeLength = 2

const utf8 = new TextEncoder()
const nameBuffer = utf8.encode(file.name)
const typeBuffer = utf8.encode(type)

const length = file.size + sizeLength
  + nameBuffer.length + typeBuffer.length + 3

const buffer = new Uint8Array(length)

现在你只需要填充缓冲区。

让我们从长度开始并复制字符串:

let i = 0
buffer[i] = nameBuffer.length
buffer.set(i += 1, nameBuffer)
buffer[i += nameBuffer.length] = typeBuffer.length
buffer.set(i += 1, typeBuffer)
buffer[i += typeBuffer.length] = sizeLength

然后必须将文件大小写为适当的Int类型:

const sizeView = new DataView(buffer)
sizeView[`setUInt${sizeLength*8}`](i += 1, file.size)

最后,复制数据:

buffer.set(array, i + sizeLength) // array is your data
© www.soinside.com 2019 - 2024. All rights reserved.