通过 socket.io (JavaScript & FileReader) 上传文件
File uploads through socket.io (JavaScript & FileReader)
我正在创建一个聊天应用程序(在 React Native 中),但目前,我已经在 vanilla JavaScript 中进行了一些测试。服务器是 NodeJS 服务器。
它适用于发送短信,但现在我对发送 photos/videos/audio 文件有一些疑问。我在网上做了很多研究,看看什么是最好的方法。
我想到了使用 FileReader API 并将文件分成块,然后通过 socket.emit()- 函数逐块发送。
到目前为止,这是我的代码(已简化):
请注意,我将创建一个 React Native 应用程序,但现在(为了测试),我刚刚创建了一个带有上传表单的 HTML 文件。
// index.html
// the page where my upload form is
var reader = {};
var file = {};
var sliceSize = 1000 * 1024;
var socket = io('http://localhost:8080');
const startUpload = e => {
e.preventDefault();
reader = new FileReader();
file = $('#file)[0].files[0]
uploadFile(0)
}
$('#start-upload').on('click', startUpload)
const uploadFile = start => {
var slice = start + sliceSize + 1;
var blob = file.slice(start, slice)
reader.on('loadend', e => {
if (slice < file.size) {
socket.emit('message', JSON.stringify({
fileName: file.name,
fileType: file.type,
fileChunk: e.target.result
})
} else {
console.log('Upload completed!')
}
})
reader.readAsDataURl(blob)
}
// app.js
// my NodeJS server-file
var file;
var files = {};
io.on('connection', socket => {
console.log('User connected!');
// when a message is received
socket.on('message', data => {
file = JSON.parse(data)
if (!files[file.fileName]) {
// this is the first chunk received
// create a new string
files[file.fileName] = '';
}
// append the binary data
files[file.fileName] = files[file.fileName] + file.fileChunk;
})
// on disconnect
socket.on('disconnect', () => {
console.log('User disconnected!');
})
})
我没有包括任何文件类型检查(我还没有到那个时候),我首先想确保这是正确的做法。
我需要做的事情:
- 从客户端向服务器发送消息(如socket.emit('uploaddone', ...))以通知服务器上传完成(并且服务器可以发出完整的文件给另一个用户)。
我的问题是:
- 可以通过套接字发送二进制数据块 (base64) 吗,还是会占用很多带宽?
- 将它们拆分成块时,我会失去一些质量(photos/videos/audio 文件)吗?
如果有更好的方法,请告诉我。我不是在要求工作代码示例,只是在好的方向上提供一些指导。
您可以通过 WebSocket 发送原始字节,base64 有 33% 的大小开销。
此外,您不必 JSON.stringify 所有(可能是大)正文并在客户端解析它。
Will I lose some quality
不,底层协议 (TCP) 会按顺序且无损坏地传送数据。
我意识到这个答案晚了几个月,但为了将来参考,您应该考虑使用 socket.io here
的确认选项
// with acknowledgement
let message = JSON.stringify({
fileName: file.name,
fileType: file.type,
fileChunk: e.target.result
})
socket.emit("message", message, (ack) => {
// send next chunk...
});
我正在创建一个聊天应用程序(在 React Native 中),但目前,我已经在 vanilla JavaScript 中进行了一些测试。服务器是 NodeJS 服务器。
它适用于发送短信,但现在我对发送 photos/videos/audio 文件有一些疑问。我在网上做了很多研究,看看什么是最好的方法。
我想到了使用 FileReader API 并将文件分成块,然后通过 socket.emit()- 函数逐块发送。
到目前为止,这是我的代码(已简化):
请注意,我将创建一个 React Native 应用程序,但现在(为了测试),我刚刚创建了一个带有上传表单的 HTML 文件。
// index.html
// the page where my upload form is
var reader = {};
var file = {};
var sliceSize = 1000 * 1024;
var socket = io('http://localhost:8080');
const startUpload = e => {
e.preventDefault();
reader = new FileReader();
file = $('#file)[0].files[0]
uploadFile(0)
}
$('#start-upload').on('click', startUpload)
const uploadFile = start => {
var slice = start + sliceSize + 1;
var blob = file.slice(start, slice)
reader.on('loadend', e => {
if (slice < file.size) {
socket.emit('message', JSON.stringify({
fileName: file.name,
fileType: file.type,
fileChunk: e.target.result
})
} else {
console.log('Upload completed!')
}
})
reader.readAsDataURl(blob)
}
// app.js
// my NodeJS server-file
var file;
var files = {};
io.on('connection', socket => {
console.log('User connected!');
// when a message is received
socket.on('message', data => {
file = JSON.parse(data)
if (!files[file.fileName]) {
// this is the first chunk received
// create a new string
files[file.fileName] = '';
}
// append the binary data
files[file.fileName] = files[file.fileName] + file.fileChunk;
})
// on disconnect
socket.on('disconnect', () => {
console.log('User disconnected!');
})
})
我没有包括任何文件类型检查(我还没有到那个时候),我首先想确保这是正确的做法。
我需要做的事情:
- 从客户端向服务器发送消息(如socket.emit('uploaddone', ...))以通知服务器上传完成(并且服务器可以发出完整的文件给另一个用户)。
我的问题是:
- 可以通过套接字发送二进制数据块 (base64) 吗,还是会占用很多带宽?
- 将它们拆分成块时,我会失去一些质量(photos/videos/audio 文件)吗?
如果有更好的方法,请告诉我。我不是在要求工作代码示例,只是在好的方向上提供一些指导。
您可以通过 WebSocket 发送原始字节,base64 有 33% 的大小开销。
此外,您不必 JSON.stringify 所有(可能是大)正文并在客户端解析它。
Will I lose some quality
不,底层协议 (TCP) 会按顺序且无损坏地传送数据。
我意识到这个答案晚了几个月,但为了将来参考,您应该考虑使用 socket.io here
的确认选项 // with acknowledgement
let message = JSON.stringify({
fileName: file.name,
fileType: file.type,
fileChunk: e.target.result
})
socket.emit("message", message, (ack) => {
// send next chunk...
});