使用 WebSocket 上传大文件

IT技术 javascript html websocket large-files fileapi
2021-03-09 21:16:02

我正在尝试使用 WebSocket API 上传大文件(至少 500MB,最好是几 GB)。问题是我不知道如何编写“发送文件的这一部分,释放使用的资源然后重复”。我希望我可以避免为此使用 Flash/Silverlight 之类的东西。

目前,我正在处理以下方面的事情:

function FileSlicer(file) {
    // randomly picked 1MB slices,
    // I don't think this size is important for this experiment
    this.sliceSize = 1024*1024;  
    this.slices = Math.ceil(file.size / this.sliceSize);

    this.currentSlice = 0;

    this.getNextSlice = function() {
        var start = this.currentSlice * this.sliceSize;
        var end = Math.min((this.currentSlice+1) * this.sliceSize, file.size);
        ++this.currentSlice;

        return file.slice(start, end);
    }
}

然后,我会上传使用:

function Uploader(url, file) {
    var fs = new FileSlicer(file);
    var socket = new WebSocket(url);

    socket.onopen = function() {
        for(var i = 0; i < fs.slices; ++i) {
            socket.send(fs.getNextSlice()); // see below
        }
    }
}

基本上这会立即返回,bufferedAmount 不变(0)并且它在尝试发送之前不断迭代并将所有切片添加到队列中;没有 socket.afterSend 可以让我正确排队,这就是我卡住的地方。

6个回答

我相信该send()方法是异步的,这就是它会立即返回的原因。要使其排队,您需要服务器在每个切片上传后将消息发送回客户端;然后客户端可以决定是否需要将下一个切片或“上传完成”消息发送回服务器。

这种事情使用 XMLHttpRequest(2) 可能会更容易;它具有内置的回调支持,并且比 WebSocket API 得到更广泛的支持。

使用 Web Worker 处理大文件,而不是在主线程中进行处理,并使用file.slice().

文章帮您解决工人的大型文件。在主线程中更改 XHR 发送到 Websocket。

//Messages from worker
function onmessage(blobOrFile) {
 ws.send(blobOrFile);
}

//construct file on server side based on blob or chunk information.
您的解决方案非常巧妙。我试过了,它非常适合 1Gb 及以上的大文件。我是作为 websocket 单元测试的一部分来做的,但是如果有人想要重用它,那么可以在那里找到源代码github.com/drogatkin/TJWS2/tree/master/1.x/test/html-js One draw当前所有发送都是异步执行的,因此您无法控制文件何时完全发送。
2021-04-21 21:16:02
WS 服务器可以在处理文件时简单地发回一条消息。它甚至可以在处理过程中发送消息来影响客户端的进度条,因为主线程没有被工作线程阻塞。
2021-05-08 21:16:02
XHR 不在主线程上运行(除非明确设置为同步运行),因此线程不是使用 Web Worker 的理由。不同之处在于 XHR 位于窗口的上下文中,如果选项卡关闭,它就会死亡,而 Web Worker 可以继续运行,直到浏览器进程终止。您可以在 Web Worker 中使用 XHR 和 WebSocket。
2021-05-15 21:16:02

为了序列化此操作,您需要服务器在每次接收和写入切片(或发生错误)时向您发送信号,这样您就可以发送下一个切片以响应onmessage事件,就像这样:

function Uploader(url, file) {
    var fs = new FileSlicer(file);
    var socket = new WebSocket(url);

    socket.onopen = function() {
       socket.send(fs.getNextSlice());
    }
    socket.onmessage = function(ms){
        if(ms.data=="ok"){
           fs.slices--;
           if(fs.slices>0) socket.send(fs.getNextSlice());
        }else{
           // handle the error code here.
        }
    }
}
@CWSpear 来自我猜的问题
2021-04-28 21:16:02
你自称FileSlicer是一个标准库,但我在任何地方都找不到。我假设那是你自己创造的东西?
2021-05-16 21:16:02

编辑:自从做出这个答案以来,网络世界、浏览器、防火墙、代理发生了很大变化。现在,可以有效地使用 websockets 发送文件,尤其是在局域网上。

Websockets 对于双向通信非常有效,特别是当您有兴趣从服务器推送信息(最好是小)时。它们充当双向套接字(因此得名)。

Websockets 看起来不像是在这种情况下使用的正确技术。特别是考虑到使用它们会增加与某些代理、浏览器 (IE) 甚至防火墙的不兼容性。

另一方面,上传文件只是将 POST 请求发送到正文中包含该文件的服务器。浏览器在这方面非常擅长,大文件的开销几乎为零。不要将 websockets 用于该任务。

我不会说你在 IETF 6455 上错了(特别是考虑到关于这个主题的搜索导致你最近努力与 websockify 中的这个新规范兼容),欢迎提供这些信息,但世界不是完全转换。看到这个代理问题此外,请在此页面上查找“浏览器支持” 基本上没有理由使用 websockets 上传文件。
2021-04-26 21:16:02
dystroy,请不要把话放在我嘴里。你的回答很好,但你的理由是有缺陷的。我没有说或暗示 WebSockets 是大文件上传的更好选择。如果您解决了这些问题,我将删除反对票。您的编辑没有改善情况。谁是“武士刀”?
2021-04-30 21:16:02
我从来没有说过 CPU。而且我知道您是新版本 websockets 的有力推动者,但是让 OP(只想上传文件)认为现在没有兼容性问题是不公平的(例如我告诉过代理)。
2021-05-04 21:16:02
如果您删除整个第二段,那么我对您的回答没有问题,但第二段大多是错误的。JSON 只是文本序列化/编码的一种方法,与 WebSockets 没有直接关系。Base64 大约大了 33%,但它并不占用大量 CPU(甚至直接在 Javascript 中执行)。肯定存在有问题的中介,但没有普遍存在的问题。唯一仍在使用 Hixie 的主流浏览器是 iOS Safari(iOS 6 可能会改变它)。Chrome、Firefox、IE 10、Opera(有但已禁用)都使用 IETF 6455。
2021-05-16 21:16:02
dystroy,您的信息已过时。标准化的 WebSocket 协议 (IETF 6455) 支持发送和接收直接二进制数据(ArrayBuffer 和 Blob)。您正在考虑仅支持发送 UTF-8 数据(需要编码二进制数据)的旧 Hixie 协议。此外,WebSocket 协议的 IETF 6455 版本专门设计用于与现有代理和防火墙进行互操作。我已经广泛使用 WebSockets,但没有看到您暗示的问题。请引用证据表明存在广泛的问题。
2021-05-17 21:16:02

如果您可以在服务器上运行 node.js,您可以使用https://github.com/binaryjs/binaryjshttps://github.com/liamks/Delivery.js