Как отправить объект JSON с ArrayBuffer в веб-сокет? - PullRequest
0 голосов
/ 22 февраля 2019

Я пытаюсь просто загрузить файл на сервер node.js.

Для этого я использую файловый API и readAsArrayBuffer.Вот код, который вызывается при возникновении события «изменение» во входном файле вместе с некоторыми функциями 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, которая является версией node.js клиентской библиотеки COBY):

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 на веб-сокет ?Я хочу отправить массив буфера вместе с именем файла и другими данными, поэтому я хочу включить его в объект JSON, но когда я JSON.stringify (/ ArrayBuffer /) результат всегда [], и IDK, как отправить ArrayBuffer с моими собственными данными ???

Кроме того, кажется, что это занимает много времени с Array.from (новый Uint8Array (arrayBufer))Как вы думаете, readAsDataURL будет быстрее?

Я могу, кстати, отправить массив буферов через ITSELF через websocket с binayType = "arraybuffer", но как мне включить имя файла с ним ??

1 Ответ

0 голосов
/ 01 марта 2019

Итак, вы хотите отправить структурированные двоичные данные.В большинстве общих двоичных форматов используется кодировка типа длина-длина ( ASN.1 или Nimn являются хорошими примерами).

В вашем случае вам может потребоваться более простаясхема, потому что у вас есть фиксированные поля: «имя», «тип», «размер», «данные».Вы уже знаете их типы.Таким образом, вы можете получить только с длиной длины.Идея состоит в том, что каждое поле в вашем байтовом потоке начинается с одного или двух байтов, содержащих длину значения.Поэтому синтаксический анализатор будет знать, сколько байтов нужно прочитать до следующего значения, что устраняет необходимость в разделителях.

Допустим, вы хотите закодировать это:

{
  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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...