對一些前端工程師來說,使用javascript上傳文件到Azure存儲中多是須要掌握的技能,但願這篇博客能給到幫助。javascript
在開始前咱們須要瞭解如下幾點:html
共享訪問簽名(Shared Access Signature(SAS)前端
Azure 存儲服務的跨域資源共享 (CORS) 支持java
共享訪問簽名jquery
共享訪問簽名,是用於提供對Windows Azure Storage中的Container,Blob,Table以及Queue在特定時間範圍內進行有限權限訪問的URL。一般狀況下,咱們訪問Azure存儲都是以帳戶名和密碼的方式來實現的,經過這種方式也給使用者包括增刪改在內的最大的訪問權限,可是在實際狀況中,咱們可能但願用戶只有讀的權限,同時咱們也不但願將帳戶名和密碼泄露出去,共享訪問簽名很好的解決了這類問題。咱們在使用Javascript上傳時就是經過共享訪問簽名的URL來進行的。ajax
Azure 存儲服務的跨域資源共享 (CORS) 支持編程
大概在2014年3月份的時候Azure已經支持CORS,通過測試目前中國版和國際版都已經支持,咱們在使用Javascript上傳的時候是將資源以PUT的方式上傳到相似於http://***.blob.core.chinacloudapi.cn這樣域名的地方,這就涉及到CORS的問題,在默認狀況下Azure存儲是禁用CORS的,因此咱們須要使用版本 2013-08-15 或更高版本設置適當的服務屬性,並向服務屬性中添加 CORS 規則,若是咱們沒有啓用CORS規則,咱們可能會獲得403錯誤,使用Fiddler工具會獲得更詳細的錯誤信息:」CORS not enabled or no matching rule found for this request」。windows
經過上面的描述,咱們在開始編程前,咱們須要SAS URL,還須要爲咱們的blob配置CORS規則,這些咱們能夠經過Azure SDK來實現,關於這部分我就不介紹了,詳細請閱讀:#SAS:http://www.windowsazure.cn/zh-cn/documentation/articles/storage-dotnet-shared-access-signature-part-2/api
若是咱們不太瞭解C#,咱們能夠藉助Azure Storage Explorer來進行配置,下面我介紹下簡單的配置:
1) 獲得SAS URL
打開Azure Storage Explorer點擊「Security」,選擇SAS失效的時間,並選擇賦予相應的權限,如上圖高亮標註的部分。
2) 配置CORS規則
打開Azure Storage Explorer鼠標停留在」Blob Containers」,這時會出現CORS設置按鈕,點擊新增CORS規則,並如上圖設置,關於具體的設置信息,請閱讀:https://msdn.microsoft.com/zh-cn/library/azure/dn535601.aspx?f=255&MSPPError=-2147217396,圖中的設置是我測試的字段,可做爲參考。
下面是具體的代碼
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>File Uploader</title> <script src="Scripts/jquery-1.10.2.min.js"></script> <script> var maxBlockSize = 256 * 1024;//Each file will be split in 256 KB. var numberOfBlocks = 1; var selectedFile = null; var currentFilePointer = 0; var totalBytesRemaining = 0; var blockIds = new Array(); var blockIdPrefix = "block-"; var submitUri = null; var bytesUploaded = 0; $(document).ready(function () { $("#output").hide(); $("#file").bind('change', handleFileSelect); if (window.File && window.FileReader && window.FileList && window.Blob) { // Great success! All the File APIs are supported. } else { alert('The File APIs are not fully supported in this browser.'); } }); //Read the file and find out how many blocks we would need to split it. function handleFileSelect(e) { maxBlockSize = 256 * 1024; currentFilePointer = 0; totalBytesRemaining = 0; var files = e.target.files; selectedFile = files[0]; $("#output").show(); $("#fileName").text(selectedFile.name); $("#fileSize").text(selectedFile.size); $("#fileType").text(selectedFile.type); var fileSize = selectedFile.size; if (fileSize < maxBlockSize) { maxBlockSize = fileSize; console.log("max block size = " + maxBlockSize); } totalBytesRemaining = fileSize; if (fileSize % maxBlockSize == 0) { numberOfBlocks = fileSize / maxBlockSize; } else { numberOfBlocks = parseInt(fileSize / maxBlockSize, 10) + 1; } console.log("total blocks = " + numberOfBlocks); var baseUrl = $("#sasUrl").val(); var indexOfQueryStart = baseUrl.indexOf("?"); submitUri = baseUrl.substring(0, indexOfQueryStart) + '/' + selectedFile.name + baseUrl.substring(indexOfQueryStart); console.log(submitUri); } var reader = new FileReader(); reader.onloadend = function (evt) { if (evt.target.readyState == FileReader.DONE) { // DONE == 2 var uri = submitUri + '&comp=block&blockid=' + blockIds[blockIds.length - 1]; var requestData = new Uint8Array(evt.target.result); $.ajax({ url: uri, type: "PUT", data: requestData, processData: false, beforeSend: function (xhr) { xhr.setRequestHeader('x-ms-blob-type', 'BlockBlob'); xhr.setRequestHeader('Content-Length', requestData.length); }, success: function (data, status) { console.log(data); console.log(status); bytesUploaded += requestData.length; var percentComplete = ((parseFloat(bytesUploaded) / parseFloat(selectedFile.size)) * 100).toFixed(2); $("#fileUploadProgress").text(percentComplete + " %"); uploadFileInBlocks(); }, error: function (xhr, desc, err) { console.log(desc); console.log(err); } }); } }; function uploadFileInBlocks() { if (totalBytesRemaining > 0) { console.log("current file pointer = " + currentFilePointer + " bytes read = " + maxBlockSize); var fileContent = selectedFile.slice(currentFilePointer, currentFilePointer + maxBlockSize); var blockId = blockIdPrefix + pad(blockIds.length, 6); console.log("block id = " + blockId); blockIds.push(btoa(blockId)); reader.readAsArrayBuffer(fileContent); currentFilePointer += maxBlockSize; totalBytesRemaining -= maxBlockSize; if (totalBytesRemaining < maxBlockSize) { maxBlockSize = totalBytesRemaining; } } else { commitBlockList(); } } function commitBlockList() { var uri = submitUri + '&comp=blocklist'; console.log(uri); var requestBody = '<?xml version="1.0" encoding="utf-8"?><BlockList>'; for (var i = 0; i < blockIds.length; i++) { requestBody += '<Latest>' + blockIds[i] + '</Latest>'; } requestBody += '</BlockList>'; console.log(requestBody); $.ajax({ url: uri, type: "PUT", data: requestBody, beforeSend: function (xhr) { xhr.setRequestHeader('x-ms-blob-content-type', selectedFile.type); xhr.setRequestHeader('Content-Length', requestBody.length); }, success: function (data, status) { console.log(data); console.log(status); }, error: function (xhr, desc, err) { console.log(desc); console.log(err); } }); } function pad(number, length) { var str = '' + number; while (str.length < length) { str = '0' + str; } return str; } </script> </head> <body> <form> <div style="margin-left: 20px;"> <h1>File Uploader</h1> <p> <strong>SAS URI</strong>: <br /> <span class="input-control text"> <input type="text" id="sasUrl" style="width: 50%" value="" /> </span> </p> <p> <strong>File To Upload</strong>: <br /> <span class="input-control text"> <input type="file" id="file" name="file" style="width: 50%" /> </span> </p> <div id="output"> <strong>File Properties:</strong> <br /> <p> Name: <span id="fileName"></span> </p> <p> File Size: <span id="fileSize"></span> bytes. </p> <p> File Type: <span id="fileType"></span> </p> <p> <input type="button" value="Upload File" onclick="uploadFileInBlocks()" /> </p> <p> <strong>Progress</strong>: <span id="fileUploadProgress">0.00 %</span> </p> </div> </div> <div> </div> </form> </body> </html>
代碼主要是將文件切成小塊,並使用Put blob操做上傳相應的小塊,最後使用Put blob list將上傳的各個小塊組成資源文件。