使用WebSocket上传大文件

Vla*_*anu 25 javascript html5 large-files websocket fileapi

我正在尝试使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我会上传使用:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上这会立即返回,bufferedAmount保持不变(0)并且它会继续迭代并在尝试发送之前将所有切片添加到队列中; 没有socket.afterSend允许我正确排队,这就是我被困住的地方.

Gra*_*ham 10

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

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


Kon*_*aju 7

使用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.
Run Code Online (Sandbox Code Playgroud)

  • 您的解决方案非常巧妙。我试过了,它非常适合 1Gb 及以上的大文件。我把它作为 websocket 单元测试的一部分,但是如果有人想要它重用,那么可以在那里找到源 https://github.com/drogatkin/TJWS2/tree/master/1.x/test/html -js 目前的一个缺点是所有发送都是异步执行的,因此您无法控制文件何时完全发送。 (2认同)

Den*_*ret 5

编辑:自从做出此答案以来,网络世界,浏览器,防火墙,代理发生了很大变化。目前,使用websocket发送文件可以高效完成,尤其是在局域网上。

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

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

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

  • dystroy,您的信息已过期。标准化的WebSocket协议(IETF 6455)支持发送和接收直接二进制数据(ArrayBuffer和Blob)。您在考虑旧的Hixie协议,该协议仅支持发送UTF-8数据(需要对二进制数据进行编码)。此外,WebSocket协议的IETF 6455版本是专门为与现有代理和防火墙互操作而设计的。我已经广泛使用了WebSockets,但是看不到您暗示的问题。请列举证据表明存在广泛的问题。 (29认同)
  • 如果您删除整个第二段,那么我的回答没有问题,但是第二段大部分是错误的。JSON只是文本序列化/编码的一种方法,与WebSockets没有直接关系。Base64大约大33%,但它不会占用大量CPU资源(即使直接在Javascript中也可以)。当然,中介机构存在漏洞,但没有广泛的问题。唯一仍在使用Hixie的狂野主流浏览器是iOS Safari(iOS 6可能会对此进行更改)。Chrome,Firefox,IE 10,Opera(有但已禁用)均使用IETF 6455。 (5认同)
  • 不好,请不要在我的嘴里放话。您的回答很好,但是您的理由有缺陷。我没有说或暗示WebSockets是上传大文件的更好选择。如果您解决了这些问题,我将删除下注。您的编辑没有改善这种情况。谁是“武士刀”? (5认同)
  • 我从没听说过CPU。而且我知道您是新版websockets的合格发起人,但是让OP(**谁只想上传文件**)认为现在不存在兼容性问题(例如,我告诉过代理人)是不公平的)。 (3认同)
  • 我不会说您对IETF 6455的理解是错误的(特别是考虑到对该主题的搜索导致您最近在努力与websockify的这一新规范兼容),欢迎您提供此信息,但世界并非如此完全转换。请参阅[此代理问题](http://stackoverflow.com/questions/10947298/redirecting-websocket-traffic-on-port-80-with-lighttpd)。此外,请在[本页](http://en.wikipedia.org/wiki/WebSocket)上查找“浏览器支持”。基本上,没有理由使用websockets上传文件。 (2认同)

Dem*_*eus 5

为了序列化此操作,您需要服务器在每次接收和写入片段(或发生错误)时向您发送信号,这样您就可以发送下一个片段以响应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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 你把`FileSlicer`看成是一个标准库,但我无法在任何地方找到它.我认为这是你自己创造的东西? (6认同)
  • @CWSpear 这是我猜的问题 (3认同)