2015-05-01 34 views
2

有一個應用程序,其中有一個類型爲「file」的輸入。以下方法獲取文件,然後準備通過AJAX將其發送到服務器。爲大文件轉換Uint8Array崩潰瀏覽器

private StartUpload = (files) => { 
    if (files && files.length === 1) { 
     this.GetFileProperties(files[0]) 
      .done((properties: IFileProperties) => { 
       $('input[type=file]').val(""); 
       if (this._compatibleTypes.indexOf(properties.Extension) >= 0) { 
        var base64 = this.ArrayBufferToBase64(properties.ArrayBuffer); 

        this.DoFileUpload(base64, properties.Extension).always(() => { 
         this.ShowDialogMessage('edit_document_upload_complete', 'edit_document_upload_complete'); 
        }); 
       } else { 
        this.ShowDialogMessage('edit_document_upload_incompatible', 'edit_document_upload_compatible_types', this._compatibleTypes); 
       } 
      }); 
    } else { 
     this.ShowDialogMessage('edit_document_upload_one_file', 'edit_document_upload_one_file_msg'); 
    } 
}; 

private ArrayBufferToBase64(buffer): any { 
    var binary = ''; 
    var bytes = new Uint8Array(buffer); 
    for (var xx = 0, len = bytes.byteLength; xx < len; xx++) { 
     binary += String.fromCharCode(bytes[xx]); 
    } 
    return window.btoa(binary); 
} 

private DoFileUpload = (base64, extension) => { 
    this.IsLoading(true); 
    var dfd = $.Deferred(); 

    var data = { 
     data: base64 
    }; 

    UpdateFormDigest((<any>window)._spPageContextInfo.webServerRelativeUrl, (<any>window)._spFormDigestRefreshInterval); 

    var methodUrl = "_vti_bin/viewfile/FileInformation.asmx/AddScannedItemAlt"; 

    $.ajax({ 
     headers: { 
      "X-RequestDigest": $("#__REQUESTDIGEST").val() 
     }, 
     url: methodUrl, 
     contentType: "application/json", 
     data: JSON.stringify(data), 
     dataType: 'json', 
     type: "POST", 
     success: (response) => { 
      // do stuff 
     }, 
     error: (e) => { 
      // do stuff 
     } 
    }); 

    return dfd; 
}; 

這在絕大多數情況下都能很好地工作。但是,當文件大小很大時(比如200MB +),會導致瀏覽器死機。

  • Chrome顯示帶有「aw snap」消息的黑灰色頁面,基本死亡。

  • IE顯示「內存不足」控制檯錯誤,但繼續工作。

  • FF顯示「無響應的腳本」警告。選擇「不要再顯示我」讓它運行,直到出現「內存不足」控制檯錯誤。

這就是它死:

for (var xx = 0, len = bytes.byteLength; xx < len; xx++) { 
    binary += String.fromCharCode(bytes[xx]); 
} 

結束語一個try/catch解決此不做任何動作,被捕獲任何錯誤。

我可以在沒有崩潰的情況下進入循環,但是從len = 210164805開始,每次迭代都很困難。爲此,我嘗試將console.log(xx)添加到循環中並讓它飛 - 但瀏覽器崩潰在任何事情出現在日誌之前。

字符串的大小是否有一定的限制,可能導致瀏覽器在超過時崩潰?

謝謝

回答

1

您需要通過在代碼塊或時間段中分解代碼以異步方式執行此操作。

這意味着你的代碼將需要使用回調,但除此之外,它是直截了當 -

var bytes = new Uint8Array(256*1024*1024); // 256 mb buffer 
 

 
convert(bytes, function(str) {    // invoke the process with a callback defined 
 
    alert("Done!"); 
 
}); 
 

 
function convert(bytes, callback) { 
 

 
    var binary = "", blockSize = 2*1024*1024, // 2 mb block 
 
     block = blockSize,     // block segment 
 
     xx = 0, len = bytes.byteLength; 
 
    
 
    (function _loop() { 
 
    while(xx < len && --block > 0) {  // copy until block segment = 0 
 
     binary += String.fromCharCode(bytes[xx++]); 
 
    } 
 
    
 
    if (xx < len) {       // more data to copy? 
 
     block = blockSize;     // reinit new block segment 
 
     binary = "";       // for demo to avoid out-of-memory 
 
     setTimeout(_loop, 10);    // KEY: async wait 
 
     
 
     // update a progress bar so we can see something going on: 
 
     document.querySelector("div").style.width = (xx/len) * 100 + "%"; 
 
    } 
 
    else callback(binary);     // if done, invoke callback 
 
    })();          // selv-invoke loop 
 
}
html, body {width:100%;margin:0;overflow:hidden} 
 
div {background:#4288F7;height:10px}
<div></div>

使用大的緩衝區轉換爲字符串,將有可能成爲客戶端運行內存不足。 200mb的緩衝區轉換爲字符串將增加2 x 200mb,因爲字符串存儲爲UTF-16(即每個字符2個字節),所以我們在這裏使用600mb開箱即用。

它取決於瀏覽器以及它如何處理內存分配以及當然系統。瀏覽器會嘗試保護計算機免受惡意腳本的攻擊,例如試圖填滿內存。

您應該可以留在ArrayBuffer中並將其發送到服務器。