2012-06-18 149 views
17

我想用WebSocket API上傳大文件(至少500MB,最好高達幾GB)。問題是,我無法弄清楚如何編寫「發送這個文件的片段,釋放使用的資源然後重複」。我希望我可以避免使用像Flash/Silverlight這樣的東西。用WebSocket上傳大文件

目前,我沿着線的東西的工作:

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讓我排隊它正確,這是我卡住的地方。

+1

假設我不想依賴於Flash/Silverlight,我應該使用什麼? XMLHttpRequest的?我的印象是WebSockets的開銷較小。 –

+2

Websockets對雙向通信的開銷較小,但是,上傳文件只是將POST請求發送到包含正文中的文件的服務器。瀏覽器非常擅長,大文件的開銷幾乎沒有。 –

+0

我正在考慮把它切成小塊。我想我會嘗試使用File API進行分片並使用XMLHttpRequest發送它,看看它是如何發生的。感謝您的幫助。如果你想用上面的信息作出回答,並且可能還有其他建議,我會樂意接受它作爲答案。 –

回答

6

編輯:網絡世界,瀏覽器,防火牆,代理服務器,改變了很多,因爲這個答案作出。目前,使用websocket 發送文件可以高效地完成,特別是在局域網上。

的WebSockets是用於雙向通信非常有效的,特別是當你有興趣在推動從服務器獲取信息(最好是較小的)。他們充當雙向套接字(因此他們的名字)。

在這種情況下,Websockets看起來並不是正確的技術。尤其是考慮到使用它們會增加與某些代理,瀏覽器(IE)或甚至防火牆的不兼容性。

另一方面,上傳一個文件只是發送一個POST請求到一個服務器與身體文件的服務器。瀏覽器非常擅長,大文件的開銷幾乎沒有。不要使用websockets進行該任務。

+22

錯誤的websockets,你的信息已經過時了。標準化的WebSocket協議(IETF 6455)支持發送和接收直接二進制數據(ArrayBuffer和Blob)。您正在考慮僅支持發送UTF-8數據(需要編碼二進制數據)的舊Hixie協議。此外,WebSocket協議的IETF 6455版本專門用於與現有代理和防火牆進行互操作。我廣泛使用了WebSocket,並沒有看到你暗示的問題。請引用證據表明存在廣泛的問題。 – kanaka

+2

我不會說你在IETF 6455上錯了(尤其是考慮到有關這個話題的搜索會導致你最近努力在websockify中與這個新規範兼容),並且這些信息是值得歡迎的,但是這個世界並非如此,完全轉換。請參閱[此代理問題](http://stackoverflow.com/questions/10947298/redirecting-websocket-traffic-on-port-80-with-lighttpd)。此外,請在[本頁](http://en.wikipedia.org/wiki/WebSocket)上查找「瀏覽器支持」。基本上沒有*理由使用websockets上傳文件。 –

+5

如果你刪除整個第二段,那麼我的答案沒有問題,但第二段主要是錯誤的。 JSON只是文本序列化/編碼的一種方法,與WebSocket沒有任何直接關係。 Base64的體積大了33%,但它並不佔用CPU(甚至直接用Javascript)。肯定有錯誤的中間人,但沒有普遍的問題。仍然使用Hixie的唯一內置主流瀏覽器是iOS Safari(iOS 6可能會改變這一點)。 Chrome,火狐,IE 10,歌劇(有但禁用)都使用IETF 6455. – kanaka

9

我相信send()方法是異步這就是爲什麼它會立即返回。爲了使其排隊,您需要服務器在每個片上傳後將消息發送回客戶端;客戶端可以決定是否需要發送下一個切片或「上傳完成」消息回到服務器。使用XMLHttpRequest(2);這樣的事情可能會更容易。它具有內置的回調支持,並且也比WebSocket API更廣泛地受到支持。

4

爲了序列化這個操作需要服務器的每一個切片收到&書面(或發生錯誤)時向您發送信號,這樣你可以在響應發送下一個切片的的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. 
     } 
    } 
} 
+5

您將'FileSlicer'製作成標準庫,但我無法在任何地方找到它。我認爲那會是你自己創造的東西? – CWSpear

7

使用網絡工作者對大文件,而不是處理在主線程做並使用file.slice()上傳文件的數據塊。

article可以幫助你處理工人的大文件。在主線程中將XHR更改爲WebSocket。

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

//construct file on server side based on blob or chunk information. 
+1

您的解決方案非常光滑。我試了一下,它對於1Gb以上的大文件大小非常合適。我是作爲websocket的單元測試的一部分,但是如果有人想要它重用,那麼可以在那裏找到源代碼https://github.com/drogatkin/TJWS2/tree/master/1.x/test/html -js目前所有發送都是異步執行的,所以當文件完全發送時你無法控制。 – Singagirl

+0

當處理文件時,WS服務器可以簡單地發回消息。它甚至可以在處理期間發送消息以在客戶端上生成進度條,因爲主線程不會被工作者阻止。 –