【純乾貨】4年前想解決的事情,今天才實驗成功

第一份軟件開發工做的第一個星期(不算作試用期的一個星期,無薪水試用)。由於不是軟件專業,也沒有通過培訓和相關工做經驗。老闆不放心,但仍是讓我試一試。作的第一件事情就是上傳文件,實時看進度,而且上傳後預覽。預覽的文件類型有word,ppt,excel,flash,視頻按幀獲取預覽圖。office文件是在服務器端轉成html後顯示出來。html

作的還滿意,就留下來了,後來就在那個公司待了兩年。html5

沒解決的事情web

上傳大文件,分塊上傳,瀏覽器原生不支持,須要藉助第三方插件。最根本的緣由就是瀏覽器端的js考慮的安全問題,不容許讀取文件內容。ajax

如今IE10和其餘主流瀏覽器支持html5了,js內置Filereader對象,websocket,這些聽聽都酷斃了。數組

重大意義的時刻:瀏覽器

我實現了原生分塊上傳,道路很是崎嶇,結果很美好。安全

這一實現標識着,瀏覽器將來無可替代的地位。服務器

我只介紹乾貨部分,其餘細節,相信有興趣的人已經知道了。websocket

首先,分塊上傳,必須能在js內讀取文件內容,filereader對象是關鍵。這是一個異步讀取方法,必須在onload事件內獲取文件內容。要真真的分塊上傳,靠onprogress讀取文件進度是不夠的,而且文件過大的時候瀏覽器會卡死。file的slice函數是關鍵,把文件內容分塊,每個onload事件觸發,標誌着一塊內容讀取完畢,且能夠在該事件內把文件進一步處理,如上傳。異步

FileReader有四個read方法,

asText,只建議用來讀取文本文件

asDataUrl,讀取到的媒體文件能夠直接用於src屬性,或者html文件內容也能夠讀成DataURL

asArrayBuffer,官方介紹說不能直接使用,須要藉助DataView,如int8Array或int32Array。對於webSocket.send方法而言,ArrayBuffer和Int32Array都是可接受類型,服務端都是byte[]類型接受;若是是普通的Array數組類型,send方法內部會先轉成字符串,服務端接受string類型,接受的內容形如[].join(',')轉換過的值。

webSocket.send方法在onload事件中調用:

client.send(this.result);

結合slice和onload事件分塊上傳的核心代碼:

         var res = this.result; loaded += res.byteLength; if (loaded < fileSize)//繼續讀取下一塊  { readBlob(loaded); times += 1; console.log("next block,times:" + times); } else { //讀取完成 console.log("done loaded:" + loaded + ",size:" + fileSize); } 

slice的使用方法:

function readBlob(start)
{
    var blob = currentFile.slice(start, start + step);
   
    reader.readAsArrayBuffer(blob);
}

關於服務端能夠接受的最大文件大小,主要由config的兩個屬性控制,MaxRequestLength和ReceiveBufferSize屬性

            ws = new WebSocketServer();//實例化WebSocketServer
            var serverConfig = new ServerConfig();
            var rootConfig = new RootConfig();
            serverConfig.MaxRequestLength =  1024 * 1024 * 1024;//1MB
            serverConfig.ReceiveBufferSize = 8096;
            serverConfig.Ip = ip;
            serverConfig.Port = port;
            ws.Setup(rootConfig, serverConfig);
       

MaxRequestLength和ReceiveBufferSize與常見的屬性不同,不追求完美的話,MaxRequestLength設置爲你但願最大可上傳的文件大小便可,分塊上傳也不須要太大。根據測試兩個屬性是有關聯的,即便MaxRequestLength比上傳的內容小一點點,也是能夠上傳的。具體待有答案後再補充。


asBinaryString 如今不同意使用的方法,且IE中也沒有該方法。

若是服務器端,不支持websocket又不想使用第三方支持如superwebsocket這樣組件,能夠直接使用ajax真真分塊上傳,肯定是每次請求都會創建新的鏈接。

使用ajax上傳注意事項乾貨:

建議把arraybuffer轉成int32array,這樣客戶端轉換較快。服務器端用int32[]接收;若是int8array服務端也能夠用int32接收。

重點:async:false 保證順序;也能夠每次上傳的時候額外帶參序號,服務器端從新組裝順序。

Int8array的話,服務端能夠直接把每一個元素轉成byte

  [System.Web.Mvc.HttpPost]
        public ActionResult File(int[] datas)
        {
            if (datas != null)
            {
                var d = datas.ToList().ConvertAll(x => (byte)x).ToArray();
            }
            return Content("OK");
        }

 

int32array的話,服務端能夠藉助bitconverter.getbytes(int)方法。

 [System.Web.Mvc.HttpPost]
        public ActionResult File(int[] datas)
        {
            if (datas != null)
            {
                List<byte> bs = new List<byte>();//接收到的文件緩衝對象
                for (int i = 0; i < datas.Length; i++)
                {
                    foreach (var item in BitConverter.GetBytes(datas[i]))
                    {
                        bs.Add(item);
                    }
                }
            }
            return Content("OK");
        }

ajax客戶端上傳關鍵方法:

  $.ajax("/Home/File", {
                data: { datas: new Int8Array(this.result) }, success: function (res) {
                    console.log(res);
                },
                async: false,
                type:"post"
            });

這個調用是位於FileReader的onload事件中。

這個只是一個文件分塊上傳的例子,更多的好處是實時通訊,如實時獲取服務器端處理進度,而不用重複請求,把長鏈接,輪詢,橋都拋在腦後吧。推送消息都成爲可能。

若是以爲有意義,別忘了點【推薦】

相關文章
相關標籤/搜索