form + iframe(項目中不多用到,本文不涉及)前端
form表單提交的方式是全部瀏覽器都支持的,藉助iframe是爲了實現不刷新界面上傳git
FormData + XHR2 + FileReader + canvasgithub
FormData接口提供了一種輕鬆構造一組表示表單字段及其值的鍵/值對的方法,而後可使用XMLHttpRequest.send()方法輕鬆地發送這些值。若是將編碼類型設置爲「multipart/form-data」,則使用與表單相同的格式。ajax
經常使用方法:編程
FormData.append(0; // 添加鍵值對 FormData.delete(); // 刪除鍵值對 FormData.entries(); // 返回容許遍歷此對象中包含的全部鍵/值對的迭代器
...具體還有不少方法能夠參考網站:FormDatacanvas
使用XMLHttpRequest (XHR)對象能夠與服務器交互。您能夠從URL獲取數據,而無需讓整個的頁面刷新。這使得Web頁面能夠只更新頁面的局部,而不影響用戶的操做。XMLHttpRequest在 Ajax 編程中被大量使用。數組
經常使用屬性和方法瀏覽器
const xhr = new XMLHttpRequest() // 經常使用屬性 xhr.onreadystatechange // 當readyState屬性發生變化時調用的函數 xhr. readyState // 存有 XMLHttpRequest 的狀態。從 0 到 4 發生變化。 0: 請求未初始化 1: 服務器鏈接已創建 2: 請求已接收 3: 請求處理中 4: 請求已完成,且響應已就緒 xhr.responseText // 包含對請求的響應,若是請求未成功或還沒有發送,則返回null xhr.timeout // 表示該請求的最大請求時間(毫秒),超過該時間請求會自動結束。 xhr.upload // 表示上傳過程。 // 經常使用方法 xhr.abort() // 停止請求 xhr.open() // 初始化一個請求。該方法只能JavaScript代碼中使用 xhr.setRequestHeader() // 設置HTTP請求頭的值 xhr.send() // 發送請求。若是請求是異步的(默認),那麼該方法將在請求發送後當即返回
更多關於XMLHttpRequest的信息點擊:XMLHttpRequest服務器
對象容許Web應用程序異步讀取存儲在用戶計算機上的文件(或原始數據緩衝區)的內容,使用 File 或 Blob 對象指定要讀取的文件或數據。app
其中File對象能夠是來自用戶在一個<input>元素上選擇文件後返回的FileList對象,也能夠來自拖放操做生成的 DataTransfer對象,還能夠是來自在一個HTMLCanvasElement上執行mozGetAsFile()方法後返回結果。
屬性:
FileReader.error // 表示在讀取文件時發生的錯誤 FileReader.readyState // 表示FileReader狀態的數字。0:尚未加載任何數據; 1:數據正在被加載;2:已完成所有的讀取請求 FileReader.result // 文件的內容。該屬性僅在讀取操做完成後纔有效,數據的格式取決於使用哪一個方法來啓動讀取操做。
事件處理
FileReader.onabort= ()=>{} // 該事件在讀取操做被中斷時觸發 FileReader.onerror = ()=>{} // 該事件在讀取操做發生錯誤時觸發。 FileReader.onload = ()=>{} // 該事件在讀取操做完成時觸發。 FileReader.onloadstart = ()=>{} // 該事件在讀取操做開始時觸發 FileReader.onloadend = ()=>{} // 該事件在讀取操做結束時(要麼成功,要麼失敗)觸發 FileReader.onprogress = ()=>{} // 該事件在讀取Blob時觸發
方法
FileReader.abort() // 停止讀取操做。在返回時,readyState屬性爲DONE FileReader.readAsArrayBuffer() // 開始讀取指定的 Blob中的內容, 一旦完成, result 屬性中保存的將是被讀取文件的 ArrayBuffer 數據對象 FileReader.readAsDataURL() // 開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含一個data: URL格式的字符串以表示所讀取文件的內容 FileReader.readAsText() // 開始讀取指定的Blob中的內容。一旦完成,result屬性中將包含一個字符串以表示所讀取的文件內容。
<form id="uploadForm" method="POST" action="upload" enctype="multipart/form-data"> <input type="file" id="myFile" name="file" /> <input type="submit" value="提交" /> </form>
全部瀏覽器都支持的上傳方式,且submit提交後頁面會刷新。
action: 提交地址
enctype的常見類型(告訴服務器咱們發送過去的數據是用哪一種格式進行編碼的)
【實現步驟】
HTML代碼
<input type="file" name="file" accept=「image/*」 onchange='handleInputChange(event)'>
一、監聽input的change事件
function handleInputChange (event) { const file = event.target.files[0]; // 獲取當前選中的文件 const imgMasSize = 1024 * 1024 * 10; // 限制大小10MB // 檢查文件類型 if(['jpeg', 'png', 'gif', 'jpg'].indexOf(file.type.split("/")[1]) < 0){ // 不支持該文件類型 } // 文件大小限制 if(file.size > imgMasSize ) { // 文件大小自定義限制 } // 圖片壓縮處理函數 transformFileToDataUrl(file); }
二、將file轉成dataUrl
function transformFileToDataUrl (file) { const imgCompassMaxSize = 200 * 1024; // 超過 200k 就壓縮 // 存儲文件相關信息 imgFile.type = file.type || 'image/jpeg'; imgFile.size = file.size; imgFile.name = file.name; imgFile.lastModifiedDate = file.lastModifiedDate; // 封裝好的函數 const reader = new FileReader(); // file轉dataUrl是個異步函數,onload表示讀取完成了 reader.onload = function(e) { const result = e.target.result; if(result.length < imgCompassMaxSize) { compress(result, processData, false ); // 圖片不壓縮 } else { compress(result, processData); // 圖片壓縮 } }; reader.readAsDataURL(file); }
三、canvas進行壓縮的處理
function compress (dataURL, callback, shouldCompress = true) { const img = new window.Image(); // new 一個圖片對象 img.src = dataURL; // 經過fileReader讀取到的base64數據 img.onload = function () { // 一、建立canvas上下文 const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 獲取圖片寬高賦值給canvas繪圖 canvas.width = img.width; canvas.height = img.height; // 繪製出一張canvas圖片 ctx.drawImage(img, 0, 0, canvas.width, canvas.height); let compressedDataUrl; if(shouldCompress){ compressedDataUrl = canvas.toDataURL(imgFile.type, 0.2); // 後面的係數是繪圖輸出圖片質量 } else { compressedDataUrl = canvas.toDataURL(imgFile.type, 1); // 不改變原圖質量 } document.getElementById('preview').appendChild(img); callback(compressedDataUrl); // 最後圖片壓縮好後,去進行base64 -> blob的轉換(傳遞到後臺) } }
四、把Blob append進FormData中;
function processData (dataURL) { const binaryString = window.atob(dataURL.split(',')[1]); // window.atob對用base-64編碼過的字符串進行解碼 const arrayBuffer = new ArrayBuffer(binaryString.length); // ArrayBuffer 對象用來表示通用的、固定長度的原始二進制數據緩衝區。ArrayBuffer 不能直接操做,而是要經過類型數組對象或 DataView 對象來操做,它們會將緩衝區中的數據表示爲特定的格式,並經過這些格式來讀寫緩衝區的內容。 const intArray = new Uint8Array(arrayBuffer); // Uint8Array類型化數組表示一個由8位無符號整數組成的數組。內容初始化爲0。一旦創建,您可使用對象的方法或使用標準數組索引語法(即便用括號符號)引用數組中的元素。 for (let i = 0, j = binaryString.length; i < j; i++) { intArray[i] = binaryString.charCodeAt(i); } const data = [intArray]; let blob; // 經過兼容性判斷,最後轉換爲二進制數據 try { blob = new Blob(data, { type: imgFile.type }); } catch (error) { window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if (error.name === 'TypeError' && window.BlobBuilder){ const builder = new BlobBuilder(); builder.append(arrayBuffer); blob = builder.getBlob(imgFile.type); } else { throw new Error('版本太低,不支持上傳圖片'); } } // blob 轉 file const fileOfBlob = new File([blob], imgFile.name); // File 對象是特殊類型的 Blob,且能夠用在任意的 Blob 類型的 context 中。 const formData = new FormData(); // 把要傳輸的數據添加到FormData()對象中 // type formData.append('type', imgFile.type); // size formData.append('size', fileOfBlob.size); // name formData.append('name', imgFile.name); // lastModifiedDate formData.append('lastModifiedDate', imgFile.lastModifiedDate); // append 文件 formData.append('file', fileOfBlob); uploadImg(formData); // 調用xhr發送數據到後臺 }
五、xhr實現上傳
function uploadImg (formData) { const xhr = new XMLHttpRequest(); // 進度監聽 xhr.upload.addEventListener('progress', (e)=>{ console.log(e, e.loaded , e.total); // 能夠利用這兩個對象算出目前的傳輸比例 }, false); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { const result = JSON.parse(xhr.responseText); if (xhr.status === 200) { // 上傳成功 console.log(result); } else { // 上傳失敗 } } }; xhr.open('POST', '/upload' , true); // 中間"/upload"爲後臺上傳地址(若是須要兼容性強可使用限制的ajax庫) xhr.send(formData); // 發送到後臺 }
代碼github訪問地址:上傳實例代碼
經過本節內容,咱們應該完全的理解了前端上傳是如何實現的,給出的代碼實例雖然簡單,可是已是很是核心,咱們能夠經過這個版本去實現一個很是複雜的需求,譬如多圖片上傳,那麼也就是遍歷的調用transformFileToDataUrl這個方法去實現。在例如添加上拖拽文件到指定區域去上傳,那麼咱們只須要了解下drag對象就能夠很輕鬆的實現了。