我试图将文件简单地上传到node.js服务器。
为此,我正在使用文件API和readAsArrayBuffer。这是在触发输入文件“ change”事件以及一些hepsler函数时调用的代码(并且我正在使用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 */))
,然后在服务器端, m重新进行JSON解析,但是如何将实际的ArrayBuffer发送到websocket ?我想发送数组缓冲区以及文件和其他数据的名称,所以我想将其包含在JSON对象中,但是当我JSON.stringify(/ ArrayBuffer /)的结果始终是[],并且IDK如何发送带有我自己的数据的ArrayBuffer ???
使用Array.from(new Uint8Array(arrayBufer))似乎也要花费很多时间,您认为readAsDataURL会更快吗?
顺便说一句,我能够通过ITSELF通过websocket发送带有binayType =“ arraybuffer”的arraybuffer,但是如何在其中包含文件名?
答案 0 :(得分:0)
因此您想发送结构化的二进制数据。大多数通用二进制格式都使用类型长度值编码(ASN.1或Nimn是很好的例子)。
在您的情况下,您可能需要一个更简单的方案,因为您具有固定的字段:“名称”,“类型”,“大小”,“数据”。您已经知道它们的类型。因此,您可以获得长度值。想法是字节流中的每个字段都以一个或两个包含值长度的字节开头。因此,解析器将知道在下一个值之前要读取多少个字节,而无需使用分隔符。
假设您要对此进行编码:
{
name: "file.txt",
type: "text/plain",
size: 4834,
data: <an ArrayBuffer of length 4834>
}
“大小”字段实际上将很有用,因为所有其他长度都适合一个字节,但内容长度却不适合。
因此,您使用字节创建了一个新的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 Buffer难一些。首先,您需要计算要发送的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